diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/build.gradle.kts index 0f5b4946004..98e3f9ca22f 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/build.gradle.kts +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/build.gradle.kts @@ -66,6 +66,9 @@ val isTeamcityBuild = project.kotlinBuildProperties.isTeamcityBuild || false } +tasks.withType().configureEach { + kotlinOptions.freeCompilerArgs += "-opt-in=kotlin.io.path.ExperimentalPathApi" +} val cleanTestKitCacheTask = tasks.register("cleanTestKitCache") { group = "Build" diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BaseGradleIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BaseGradleIT.kt index 260b202fab5..8c70a66819e 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BaseGradleIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BaseGradleIT.kt @@ -11,15 +11,16 @@ import org.gradle.api.logging.configuration.WarningMode import org.gradle.tooling.GradleConnector import org.gradle.util.GradleVersion import org.intellij.lang.annotations.Language -import org.jdom.Element -import org.jdom.input.SAXBuilder -import org.jdom.output.Format -import org.jdom.output.XMLOutputter import org.jetbrains.kotlin.gradle.model.ModelContainer import org.jetbrains.kotlin.gradle.model.ModelFetcherBuildAction import org.jetbrains.kotlin.gradle.plugin.KotlinJsCompilerType import org.jetbrains.kotlin.gradle.testbase.enableCacheRedirector +import org.jetbrains.kotlin.gradle.testbase.extractJavaCompiledSources +import org.jetbrains.kotlin.gradle.testbase.extractKotlinCompiledSources +import org.jetbrains.kotlin.gradle.testbase.prettyPrintXml +import org.jetbrains.kotlin.gradle.testbase.readAndCleanupTestResults import org.jetbrains.kotlin.gradle.util.* +import org.jetbrains.kotlin.gradle.util.modify import org.jetbrains.kotlin.test.RunnerWithMuteInDatabase import org.jetbrains.kotlin.test.util.trimTrailingWhitespaces import org.junit.After @@ -29,6 +30,7 @@ import org.junit.Before import org.junit.runner.RunWith import java.io.File import java.util.regex.Pattern +import kotlin.io.path.isDirectory import kotlin.test.* val SYSTEM_LINE_SEPARATOR: String = System.getProperty("line.separator") @@ -372,24 +374,11 @@ abstract class BaseGradleIT { } class CompiledProject(val project: Project, val output: String, val resultCode: Int) { - companion object { - val kotlinSourcesListRegex = Regex("\\[KOTLIN\\] compile iteration: ([^\\r\\n]*)") - val javaSourcesListRegex = Regex("\\[DEBUG\\] \\[[^\\]]*JavaCompiler\\] Compiler arguments: ([^\\r\\n]*)") - } - - private fun getCompiledFiles(regex: Regex, output: String) = regex.findAll(output) - .asIterable() - .flatMap { - it.groups[1]!!.value.split(", ") - .map { File(project.projectDir, it).canonicalFile } - } - - fun getCompiledKotlinSources(output: String) = getCompiledFiles(kotlinSourcesListRegex, output) + fun getCompiledKotlinSources(output: String) = + extractKotlinCompiledSources(project.projectDir.toPath(), output).map { sourcePath -> sourcePath.toFile() } val compiledJavaSources: Iterable by lazy { - javaSourcesListRegex.findAll(output).asIterable().flatMap { - it.groups[1]!!.value.split(" ").filter { it.endsWith(".java", ignoreCase = true) }.map { File(it).canonicalFile } - } + extractJavaCompiledSources(output).map { sourcePath -> sourcePath.toFile() } } } @@ -846,71 +835,20 @@ Finished executing task ':$taskName'| vararg testReportNames: String ) { val projectDir = project.projectDir - val testReportDirs = testReportNames.map { projectDir.resolve("build/test-results/$it") } + val testReportDirs = testReportNames.map { projectDir.resolve("build/test-results/$it").toPath() } testReportDirs.forEach { - if (!it.isDirectory) { + if (!it.isDirectory()) { error("Test report dir $it was not created") } } - val actualTestResults = readAndCleanupTestResults(testReportDirs, projectDir) + val actualTestResults = readAndCleanupTestResults(testReportDirs, projectDir.toPath()) val expectedTestResults = prettyPrintXml(resourcesRootFile.resolve(assertionFileName).readText()) assertEquals(expectedTestResults, actualTestResults) } - private fun readAndCleanupTestResults(testReportDirs: List, projectDir: File): String { - val files = testReportDirs - .flatMap { - (it.listFiles() ?: arrayOf()).filter { - it.isFile && it.name.endsWith(".xml") - } - } - .sortedBy { - // let containing test suite be first - it.name.replace(".xml", ".A.xml") - } - - val xmlString = buildString { - appendLine("") - appendLine("") - files.forEach { file -> - appendLine( - file.readText() - .trimTrailingWhitespaces() - .replace(projectDir.absolutePath, "/\$PROJECT_DIR$") - .replace(projectDir.name, "\$PROJECT_NAME$") - .replace("\n", "") - ) - } - appendLine("") - } - - val doc = SAXBuilder().build(xmlString.reader()) - val skipAttrs = setOf("timestamp", "hostname", "time", "message") - val skipContentsOf = setOf("failure") - - fun cleanup(e: Element) { - if (e.name in skipContentsOf) e.text = "..." - e.attributes.forEach { - if (it.name in skipAttrs) { - it.value = "..." - } - } - - e.children.forEach { - cleanup(it) - } - } - - cleanup(doc.rootElement) - return XMLOutputter(Format.getPrettyFormat()).outputString(doc) - } - - private fun prettyPrintXml(uglyXml: String): String = - XMLOutputter(Format.getPrettyFormat()).outputString(SAXBuilder().build(uglyXml.reader())) - private fun Project.createGradleTailParameters(options: BuildOptions, params: Array = arrayOf()): List = params.toMutableList().apply { add("--stacktrace") diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/ConfigurationCacheIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/ConfigurationCacheIT.kt index 4a1673accb1..265a7e310a2 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/ConfigurationCacheIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/ConfigurationCacheIT.kt @@ -148,7 +148,6 @@ class ConfigurationCacheIT : AbstractConfigurationCacheIT() { fun testConfigurationCacheJsPlugin(gradleVersion: GradleVersion) { project("kotlin-js-browser-project", gradleVersion) { buildGradleKts.modify(::transformBuildScriptWithPluginsDsl) - settingsGradleKts.modify(::transformBuildScriptWithPluginsDsl) testConfigurationCacheOf( ":app:build", @@ -187,7 +186,6 @@ class ConfigurationCacheIT : AbstractConfigurationCacheIT() { configure: TestProject.() -> Unit = {} ) = project("dukat-integration/both", gradleVersion) { buildGradleKts.modify(::transformBuildScriptWithPluginsDsl) - settingsGradleKts.modify(::transformBuildScriptWithPluginsDsl) configure(this) testConfigurationCacheOf( "irGenerateExternalsIntegrated", diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kotlin2JsGradlePluginIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kotlin2JsGradlePluginIT.kt index 1ffafd3ca3b..e7a6041abc8 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kotlin2JsGradlePluginIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kotlin2JsGradlePluginIT.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.gradle import com.google.gson.Gson 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 @@ -18,105 +19,89 @@ import org.jetbrains.kotlin.gradle.targets.js.npm.PackageJson import org.jetbrains.kotlin.gradle.targets.js.npm.fromSrcPackageJson import org.jetbrains.kotlin.gradle.tasks.USING_JS_INCREMENTAL_COMPILATION_MESSAGE import org.jetbrains.kotlin.gradle.tasks.USING_JS_IR_BACKEND_MESSAGE -import org.jetbrains.kotlin.gradle.util.* -import org.junit.Assert -import org.junit.Assume.assumeFalse -import org.junit.Ignore -import org.junit.Test -import java.io.File -import java.io.FileFilter +import org.jetbrains.kotlin.gradle.testbase.* +import org.jetbrains.kotlin.gradle.util.jsCompilerType +import org.jetbrains.kotlin.gradle.util.normalizePath +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.condition.DisabledIf +import java.nio.file.Files +import java.nio.file.Paths import java.util.zip.ZipFile +import kotlin.io.path.appendText +import kotlin.io.path.extension +import kotlin.io.path.name +import kotlin.io.path.readText +import kotlin.streams.toList import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertTrue +@SimpleGradlePluginTests class Kotlin2JsIrGradlePluginIT : AbstractKotlin2JsGradlePluginIT(true) { - override fun defaultBuildOptions(): BuildOptions { - return super.defaultBuildOptions().copy(warningMode = WarningMode.Summary) - } + @DisplayName("TS type declarations are generated") + @GradleTest + fun generateDts(gradleVersion: GradleVersion) { + project("kotlin2JsIrDtsGeneration", gradleVersion) { + build("build") { + checkIrCompilationMessage() - @Test - fun generateDts() { - val project = Project("kotlin2JsIrDtsGeneration") - project.build("build") { - assertSuccessful() - checkIrCompilationMessage() - - assertFileExists("build/kotlin2js/main/lib.js") - val dts = fileInWorkingDir("build/kotlin2js/main/lib.d.ts") - assert(dts.exists()) - assert(dts.readText().contains("function bar(): string")) + assertFileInProjectExists("build/kotlin2js/main/lib.js") + val dts = projectPath.resolve("build/kotlin2js/main/lib.d.ts") + assertFileExists(dts) + assertFileContains(dts, "function bar(): string") + } } } - @Test - fun testCleanOutputWithEmptySources() { - with(Project("kotlin-js-nodejs-project")) { - setupWorkingDir() - gradleBuildScript().modify(::transformBuildScriptWithPluginsDsl) - gradleSettingsScript().modify(::transformBuildScriptWithPluginsDsl) - - build( - "build", - options = defaultBuildOptions().copy(jsCompilerType = KotlinJsCompilerType.IR) - ) { - assertSuccessful() - + @DisplayName("stale output is cleaned") + @GradleTest + fun testCleanOutputWithEmptySources(gradleVersion: GradleVersion) { + project("kotlin-js-nodejs-project", gradleVersion) { + build("build") { assertTasksExecuted(":compileProductionExecutableKotlinJs") - assertFileExists("build/js/packages/kotlin-js-nodejs/kotlin/kotlin-js-nodejs.js") + assertFileInProjectExists("build/js/packages/kotlin-js-nodejs-project/kotlin/kotlin-js-nodejs-project.js") } - File("${projectDir.canonicalPath}/src").deleteRecursively() + projectPath.resolve("src").toFile().deleteRecursively() - gradleBuildScript().appendText( - """${"\n"} - tasks.named("compileProductionExecutableKotlinJs").configure { - mode = org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsBinaryMode.DEVELOPMENT - } - """.trimIndent() + buildGradle.appendText( + """ + | + |tasks.named("compileProductionExecutableKotlinJs").configure { + | mode = org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsBinaryMode.DEVELOPMENT + |} + """.trimMargin() ) - build( - "build", - options = defaultBuildOptions().copy(jsCompilerType = KotlinJsCompilerType.IR) - ) { - assertSuccessful() - - assertNoSuchFile("build/js/packages/kotlin-js-nodejs/kotlin/") + build("build") { + assertFileNotExists("build/js/packages/kotlin-js-nodejs/kotlin/") } } } - @Test - fun testJsCompositeBuild() { - val rootProjectName = "js-composite-build" - val appProject = transformProjectWithPluginsDsl( - projectName = "app", - directoryPrefix = rootProjectName, - ) + @DisplayName("js composite build works") + @GradleTest + fun testJsCompositeBuild(gradleVersion: GradleVersion) { + project("js-composite-build", gradleVersion) { + buildGradleKts.modify(::transformBuildScriptWithPluginsDsl) + gradleProperties.appendText(jsCompilerType(KotlinJsCompilerType.IR)) - val libProject = transformProjectWithPluginsDsl( - projectName = "lib", - directoryPrefix = rootProjectName, - ) + subProject("lib").apply { + buildGradleKts.modify(::transformBuildScriptWithPluginsDsl) + gradleProperties.appendText(jsCompilerType(KotlinJsCompilerType.IR)) + } - libProject.gradleProperties().appendText(jsCompilerType(KotlinJsCompilerType.IR)) - appProject.gradleProperties().appendText(jsCompilerType(KotlinJsCompilerType.IR)) + fun asyncVersion(rootModulePath: String, moduleName: String): String = + NpmProjectModules(projectPath.resolve(rootModulePath).toFile()) + .require(moduleName) + .let { Paths.get(it).parent.parent.resolve(NpmProject.PACKAGE_JSON) } + .let { fromSrcPackageJson(it.toFile()) } + .let { it!!.version } - libProject.projectDir.copyRecursively(appProject.projectDir.resolve(libProject.projectDir.name)) - - fun Project.asyncVersion(rootModulePath: String, moduleName: String): String = - NpmProjectModules(projectDir.resolve(rootModulePath)) - .require(moduleName) - .let { File(it).parentFile.parentFile.resolve(NpmProject.PACKAGE_JSON) } - .let { fromSrcPackageJson(it) } - .let { it!!.version } - - with(appProject) { build("build") { - assertSuccessful() val appAsyncVersion = asyncVersion("build/js/node_modules/app", "async") assertEquals("3.2.0", appAsyncVersion) @@ -126,954 +111,932 @@ class Kotlin2JsIrGradlePluginIT : AbstractKotlin2JsGradlePluginIT(true) { } } - @Test - @Ignore // Persistent IR is no longer supported - fun testJsIrIncrementalInParallel() = with(Project("kotlin-js-browser-project")) { - setupWorkingDir() - gradleBuildScript().modify(::transformBuildScriptWithPluginsDsl) - gradleSettingsScript().modify(::transformBuildScriptWithPluginsDsl) - gradleProperties().appendText("\nkotlin.incremental.js.ir=true") - gradleProperties().appendText("\norg.gradle.parallel=true") + @GradleTest + @Disabled // Persistent IR is no longer supported + fun testJsIrIncrementalInParallel(gradleVersion: GradleVersion) { + project("kotlin-js-browser-project", gradleVersion) { + buildGradleKts.modify(::transformBuildScriptWithPluginsDsl) + gradleProperties.appendText( + """ + | + |kotlin.incremental.js.ir=true + |org.gradle.parallel=true + """.trimMargin() + ) - build( - "assemble", - options = defaultBuildOptions() - .copy( - jsCompilerType = KotlinJsCompilerType.IR, - warningMode = WarningMode.Fail - ) - ) { - assertSuccessful() + build("assemble") } } } +@SimpleGradlePluginTests class Kotlin2JsGradlePluginIT : AbstractKotlin2JsGradlePluginIT(false) { - override fun defaultBuildOptions(): BuildOptions { - return super.defaultBuildOptions().copy(warningMode = WarningMode.Summary) - } - - @Test - fun testIncrementalCompilation() = Project("kotlin2JsICProject").run { - setupWorkingDir() - val modules = listOf("app", "lib") - val mainFiles = modules.flatMapTo(LinkedHashSet()) { - projectDir.resolve("$it/src/main").allKotlinFiles() - } - - build("build") { - assertSuccessful() - checkIrCompilationMessage() - assertContains(USING_JS_INCREMENTAL_COMPILATION_MESSAGE) - if (irBackend) { - assertCompiledKotlinSources(project.relativize(mainFiles)) - } else { - assertCompiledKotlinSources(project.relativize(allKotlinFiles)) + @DisplayName("incremental compilation for js works") + @GradleTest + fun testIncrementalCompilation(gradleVersion: GradleVersion) { + val buildOptions = defaultBuildOptions.copy(logLevel = LogLevel.DEBUG) + project("kotlin2JsICProject", gradleVersion, buildOptions = buildOptions) { + val modules = listOf("app", "lib") + val mainFiles = modules.flatMapTo(LinkedHashSet()) { + projectPath.resolve("$it/src/main").allKotlinFiles } - } - build("build") { - assertSuccessful() - assertCompiledKotlinSources(emptyList()) - } + build("build") { + checkIrCompilationMessage() + assertOutputContains(USING_JS_INCREMENTAL_COMPILATION_MESSAGE) + if (irBackend) { + assertCompiledKotlinSources(mainFiles.relativizeTo(projectPath), output) + } else { + assertCompiledKotlinSources(projectPath.allKotlinFiles.relativizeTo(projectPath), output) + } + } - projectFile("A.kt").modify { - it.replace("val x = 0", "val x = \"a\"") - } - build("build") { - assertSuccessful() - checkIrCompilationMessage() - assertContains(USING_JS_INCREMENTAL_COMPILATION_MESSAGE) - val affectedFiles = project.projectDir.getFilesByNames("A.kt", "useAInLibMain.kt", "useAInAppMain.kt", "useAInAppTest.kt") - if (irBackend) { - // only klib ic is supported for now, so tests are generated non-incrementally with ir backend - assertCompiledKotlinSources(project.relativize(affectedFiles.filter { it in mainFiles })) - } else { - assertCompiledKotlinSources(project.relativize(affectedFiles)) + build("build") { + assertCompiledKotlinSources(emptyList(), output) + } + + val modifiedFile = subProject("lib").kotlinSourcesDir().resolve("A.kt") ?: error("No A.kt file in test project") + modifiedFile.modify { + it.replace("val x = 0", "val x = \"a\"") + } + build("build") { + checkIrCompilationMessage() + assertOutputContains(USING_JS_INCREMENTAL_COMPILATION_MESSAGE) + val affectedFiles = listOf("A.kt", "useAInLibMain.kt", "useAInAppMain.kt", "useAInAppTest.kt").mapNotNull { + projectPath.findInPath(it) + } + if (irBackend) { + // only klib ic is supported for now, so tests are generated non-incrementally with ir backend + assertCompiledKotlinSources(affectedFiles.filter { it in mainFiles }.relativizeTo(projectPath), output) + } else { + assertCompiledKotlinSources(affectedFiles.relativizeTo(projectPath), output) + } } } } - @Test - fun testKotlinJsBuiltins() { - val project = Project("kotlinBuiltins") - - project.setupWorkingDir() - val buildGradle = File(project.projectDir, "app").getFileByName("build.gradle") - buildGradle.modify { - it.replace("apply plugin: \"kotlin\"", "apply plugin: \"kotlin2js\"") + - "\ncompileKotlin2Js.kotlinOptions.outputFile = \"out/out.js\"" - } - project.build("build") { - assertSuccessful() + @DisplayName("builtins are loaded") + @GradleTest + fun testKotlinJsBuiltins(gradleVersion: GradleVersion) { + project("kotlinBuiltins", gradleVersion) { + subProject("app").buildGradle.modify { originalScript -> + buildString { + append(originalScript.replace("apply plugin: \"kotlin\"", "apply plugin: \"kotlin2js\"")) + append( + """ + | + |compileKotlin2Js.kotlinOptions.outputFile = "out/out.js" + """.trimMargin() + ) + } + } + build("build") } } - @Test - fun testDce() { - val project = Project("kotlin2JsDceProject", minLogLevel = LogLevel.INFO) + @DisplayName("DCE minifies output file") + @GradleTest + fun testDce(gradleVersion: GradleVersion) { + project("kotlin2JsDceProject", gradleVersion) { + build("runRhino") { + val pathPrefix = "mainProject/build/kotlin-js-min/main" + assertFileInProjectExists("$pathPrefix/exampleapp.js.map") + assertFileInProjectExists("$pathPrefix/examplelib.js.map") + assertFileInProjectContains("$pathPrefix/exampleapp.js.map", "\"../../../src/main/kotlin/exampleapp/main.kt\"") - project.build("runRhino") { - assertSuccessful() - val pathPrefix = "mainProject/build/kotlin-js-min/main" - assertFileExists("$pathPrefix/exampleapp.js.map") - assertFileExists("$pathPrefix/examplelib.js.map") - assertFileContains("$pathPrefix/exampleapp.js.map", "\"../../../src/main/kotlin/exampleapp/main.kt\"") - - assertFileExists("$pathPrefix/kotlin.js") - assertTrue(fileInWorkingDir("$pathPrefix/kotlin.js").length() < 500 * 1000, "Looks like kotlin.js file was not minified by DCE") + val kotlinJs = projectPath.resolve("$pathPrefix/kotlin.js") + assertFileExists(kotlinJs) + assertTrue( + Files.size(kotlinJs) < 500 * 1000, + "Looks like kotlin.js file was not minified by DCE" + ) + } } } - @Test - fun testDceOutputPath() { - val project = Project("kotlin2JsDceProject", minLogLevel = LogLevel.INFO) + @DisplayName("DCE output directory can be changed") + @GradleTest + fun testDceOutputPath(gradleVersion: GradleVersion) { + project("kotlin2JsDceProject", gradleVersion) { + subProject("mainProject").buildGradle.modify { originalScript -> + buildString { + append(originalScript) + append( + """ + | + |runDceKotlinJs.dceOptions.outputDirectory = "${'$'}{buildDir}/min" + |runRhino.args = ["-f", "min/kotlin.js", "-f", "min/examplelib.js", "-f", "min/exampleapp.js", "-f", "../check.js"] + """.trimMargin() + ) + } + } - project.setupWorkingDir() - File(project.projectDir, "mainProject/build.gradle").modify { - it + "\n" + - "runDceKotlinJs.dceOptions.outputDirectory = \"\${buildDir}/min\"\n" + - "runRhino.args = [\"-f\", \"min/kotlin.js\", \"-f\", \"min/examplelib.js\", \"-f\", \"min/exampleapp.js\"," + - "\"-f\", \"../check.js\"]\n" - } + build("runRhino") { + val pathPrefix = "mainProject/build/min" + assertFileInProjectExists("$pathPrefix/examplelib.js.map") + assertFileInProjectContains("$pathPrefix/exampleapp.js.map", "\"../../src/main/kotlin/exampleapp/main.kt\"") - project.build("runRhino") { - assertSuccessful() - val pathPrefix = "mainProject/build/min" - assertFileExists("$pathPrefix/exampleapp.js.map") - assertFileExists("$pathPrefix/examplelib.js.map") - assertFileContains("$pathPrefix/exampleapp.js.map", "\"../../src/main/kotlin/exampleapp/main.kt\"") - - assertFileExists("$pathPrefix/kotlin.js") - assertTrue(fileInWorkingDir("$pathPrefix/kotlin.js").length() < 500 * 1000, "Looks like kotlin.js file was not minified by DCE") + val kotlinJs = projectPath.resolve("$pathPrefix/kotlin.js") + assertFileExists(kotlinJs) + assertTrue(Files.size(kotlinJs) < 500 * 1000, "Looks like kotlin.js file was not minified by DCE") + } } } - @Test - fun testDceDevMode() { - val project = Project("kotlin2JsDceProject", minLogLevel = LogLevel.INFO) + @DisplayName("DCE in dev mode doesn't minify output file") + @GradleTest + fun testDceDevMode(gradleVersion: GradleVersion) { + project("kotlin2JsDceProject", gradleVersion) { + subProject("mainProject").buildGradle.appendText( + """ + | + |runDceKotlinJs.dceOptions.devMode = true + | + """.trimMargin() + ) - project.setupWorkingDir() - File(project.projectDir, "mainProject/build.gradle").modify { - it + "\n" + - "runDceKotlinJs.dceOptions.devMode = true\n" - } + build("runRhino") { + val pathPrefix = "mainProject/build/kotlin-js-min/main" + assertFileInProjectExists("$pathPrefix/examplelib.js.map") + assertFileInProjectContains("$pathPrefix/exampleapp.js.map", "\"../../../src/main/kotlin/exampleapp/main.kt\"") - project.build("runRhino") { - assertSuccessful() - val pathPrefix = "mainProject/build/kotlin-js-min/main" - assertFileExists("$pathPrefix/exampleapp.js.map") - assertFileExists("$pathPrefix/examplelib.js.map") - assertFileContains("$pathPrefix/exampleapp.js.map", "\"../../../src/main/kotlin/exampleapp/main.kt\"") - - assertFileExists("$pathPrefix/kotlin.js") - assertTrue(fileInWorkingDir("$pathPrefix/kotlin.js").length() > 1000 * 1000, "Looks like kotlin.js file was minified by DCE") + val kotlinJs = projectPath.resolve("$pathPrefix/kotlin.js") + assertFileExists(kotlinJs) + assertTrue(Files.size(kotlinJs) > 1000 * 1000, "Looks like kotlin.js file was minified by DCE") + } } } - @Test - fun testDceFileCollectionDependency() { - val project = Project("kotlin2JsDceProject", minLogLevel = LogLevel.INFO) + @DisplayName("DCE minifies FileCollection dependencies") + @GradleTest + fun testDceFileCollectionDependency(gradleVersion: GradleVersion) { + project("kotlin2JsDceProject", gradleVersion) { + subProject("mainProject").buildGradle.modify { + it.replace("compile project(\":libraryProject\")", "compile project(\":libraryProject\").sourceSets.main.output") + } - project.setupWorkingDir() - File(project.projectDir, "mainProject/build.gradle").modify { - it.replace("compile project(\":libraryProject\")", "compile project(\":libraryProject\").sourceSets.main.output") - } + build("runRhino") { + val pathPrefix = "mainProject/build/kotlin-js-min/main" + assertFileInProjectExists("$pathPrefix/examplelib.js.map") + assertFileInProjectContains("$pathPrefix/exampleapp.js.map", "\"../../../src/main/kotlin/exampleapp/main.kt\"") - project.build("runRhino") { - assertSuccessful() - val pathPrefix = "mainProject/build/kotlin-js-min/main" - assertFileExists("$pathPrefix/exampleapp.js.map") - assertFileExists("$pathPrefix/examplelib.js.map") - assertFileContains("$pathPrefix/exampleapp.js.map", "\"../../../src/main/kotlin/exampleapp/main.kt\"") - - assertFileExists("$pathPrefix/kotlin.js") - assertTrue(fileInWorkingDir("$pathPrefix/kotlin.js").length() < 500 * 1000, "Looks like kotlin.js file was not minified by DCE") + val kotlinJs = projectPath.resolve("$pathPrefix/kotlin.js") + assertFileExists(kotlinJs) + assertTrue(Files.size(kotlinJs) < 500 * 1000, "Looks like kotlin.js file was not minified by DCE") + } } } - @Test - fun testKotlinJsDependencyWithJsFiles() = with(Project("kotlin-js-dependency-with-js-files")) { - setupWorkingDir() - gradleBuildScript().modify(::transformBuildScriptWithPluginsDsl) + @DisplayName("js files from dependency are installed") + @GradleTest + fun testKotlinJsDependencyWithJsFiles(gradleVersion: GradleVersion) { + project("kotlin-js-dependency-with-js-files", gradleVersion) { + build("packageJson") { + val dependency = "2p-parser-core" + val version = "0.11.1" - build( - "packageJson" - ) { - assertSuccessful() + val dependencyDirectory = projectPath.resolve("build/js/packages_imported/$dependency/$version") + assertDirectoryExists(dependencyDirectory) - val dependency = "2p-parser-core" - val version = "0.11.1" - - assertFileExists("build/js/packages_imported/$dependency/$version") - - fun getPackageJson(dependency: String, version: String) = - fileInWorkingDir("build/js/packages_imported/$dependency/$version") + val packageJson = dependencyDirectory .resolve(NpmProject.PACKAGE_JSON) .let { Gson().fromJson(it.readText(), PackageJson::class.java) } + assertEquals(dependency, packageJson.name) + assertEquals(version, packageJson.version) + assertEquals("$dependency.js", packageJson.main) + } + } + } - val packageJson = getPackageJson(dependency, version) + @DisplayName("DCE in dev mode replaces outdated dependencies on incremental build") + @GradleTest + fun testIncrementalDceDevModeOnExternalDependency(gradleVersion: GradleVersion) { + project("kotlin-js-browser-project", gradleVersion) { + buildGradleKts.modify(::transformBuildScriptWithPluginsDsl) - assertEquals(dependency, packageJson.name) - assertEquals(version, packageJson.version) - assertEquals("$dependency.js", packageJson.main) + build(":base:jsLegacyJar") + + val baseSubproject = subProject("base") + val libSubproject = subProject("lib") + val baseJar = baseSubproject.projectPath.resolve("build/libs/base-legacy.jar") + val originalBaseJar = libSubproject.projectPath.resolve("base.1.jar") + val modifiedBaseJar = libSubproject.projectPath.resolve("base.2.jar") + Files.copy(baseJar, originalBaseJar) + + baseSubproject.kotlinSourcesDir().resolve("Base.kt").appendText( + """ + | + |fun bestRandom() = 4 + """.trimMargin() + ) + + build(":base:jsLegacyJar") + + Files.copy(baseJar, modifiedBaseJar) + + val libBuildscript = libSubproject.buildGradleKts + libBuildscript.modify { + it.replace("implementation(project(\":base\"))", "implementation(files(\"${normalizePath(originalBaseJar.toString())}\"))") + } + libBuildscript.appendText( + """ + kotlin.js().browser { + dceTask { + dceOptions.devMode = true + } + } + """.trimMargin() + ) + + val baseDceFile = projectPath.resolve("build/js/packages/kotlin-js-browser-lib/kotlin-dce/kotlin-js-browser-base-js-legacy.js") + + build(":lib:processDceKotlinJs") { + assertFileDoesNotContain(baseDceFile, "bestRandom") + } + + libBuildscript.modify { + it.replace(normalizePath(originalBaseJar.toString()), normalizePath(modifiedBaseJar.toString())) + } + + build(":lib:processDceKotlinJs") { + assertFileContains(baseDceFile, "bestRandom") + } } } } -abstract class AbstractKotlin2JsGradlePluginIT(val irBackend: Boolean) : BaseGradleIT() { - override fun defaultBuildOptions(): BuildOptions = - super.defaultBuildOptions().copy( - jsIrBackend = irBackend, - jsCompilerType = if (irBackend) KotlinJsCompilerType.IR else KotlinJsCompilerType.LEGACY +abstract class AbstractKotlin2JsGradlePluginIT(protected val irBackend: Boolean) : KGPBaseTest() { + private val defaultJsOptions = BuildOptions.JsOptions( + useIrBackend = irBackend, + jsCompilerType = if (irBackend) KotlinJsCompilerType.IR else KotlinJsCompilerType.LEGACY, + ) + + override val defaultBuildOptions = + super.defaultBuildOptions.copy( + jsOptions = defaultJsOptions, + warningMode = WarningMode.Summary ) - protected fun CompiledProject.checkIrCompilationMessage() { + protected fun BuildResult.checkIrCompilationMessage() { if (irBackend) { - assertContains(USING_JS_IR_BACKEND_MESSAGE) + assertOutputContains(USING_JS_IR_BACKEND_MESSAGE) } else { - assertNotContains(USING_JS_IR_BACKEND_MESSAGE) + assertOutputDoesNotContain(USING_JS_IR_BACKEND_MESSAGE) } } - @Test - fun testJarIncludesJsDefaultOutput() { - val project = Project("kotlin2JsNoOutputFileProject") + @DisplayName("js default output is included into jar") + @GradleTest + fun testJarIncludesJsDefaultOutput(gradleVersion: GradleVersion) { + project("kotlin2JsNoOutputFileProject", gradleVersion) { + build("jar") { + checkIrCompilationMessage() - project.build("jar") { - assertSuccessful() - checkIrCompilationMessage() + assertTasksExecuted(":compileKotlin2Js") + val jarPath = projectPath.resolve("build/libs/kotlin2JsNoOutputFileProject.jar") + assertFileExists(jarPath) + ZipFile(jarPath.toFile()).use { jar -> + if (!irBackend) { + assertEquals( + 1, jar.entries().asSequence().count { it.name == "kotlin2JsNoOutputFileProject.js" }, + "The jar should contain an entry `kotlin2JsNoOutputFileProject.js` with no duplicates" + ) + } + } + } + } + } - assertTasksExecuted(":compileKotlin2Js") - val jarPath = "build/libs/kotlin2JsNoOutputFileProject.jar" - assertFileExists(jarPath) - val jar = ZipFile(fileInWorkingDir(jarPath)) - if (!irBackend) { - assertEquals( - 1, jar.entries().asSequence().count { it.name == "kotlin2JsNoOutputFileProject.js" }, - "The jar should contain an entry `kotlin2JsNoOutputFileProject.js` with no duplicates" + @DisplayName("js customized output is included into jar") + @GradleTest + fun testJarIncludesJsOutputSetExplicitly(gradleVersion: GradleVersion) { + project("kotlin2JsModuleKind", gradleVersion) { + build(":jar") { + checkIrCompilationMessage() + + assertTasksExecuted(":compileKotlin2Js") + val jarPath = projectPath.resolve("build/libs/kotlin2JsModuleKind.jar") + assertFileExists(jarPath) + ZipFile(jarPath.toFile()).use { jar -> + assertEquals( + 1, jar.entries().asSequence().count { it.name == "app.js" }, + "The jar should contain an entry `app.js` with no duplicates" + ) + } + } + } + } + + @DisplayName("moduleKind option works") + @GradleTest + fun testModuleKind(gradleVersion: GradleVersion) { + project("kotlin2JsModuleKind", gradleVersion) { + build("runRhino") { + checkIrCompilationMessage() + } + } + } + + @DisplayName("default output file is generated") + @GradleTest + fun testDefaultOutputFile(gradleVersion: GradleVersion) { + project("kotlin2JsNoOutputFileProject", gradleVersion) { + build("build") { + checkIrCompilationMessage() + if (!irBackend) { + assertFileExists(kotlinClassesDir().resolve("kotlin2JsNoOutputFileProject.js")) + } + assertFileExists(kotlinClassesDir(sourceSet = "test").resolve("kotlin2JsNoOutputFileProject_test.js")) + } + } + } + + @DisplayName("test compilation can access main compilation") + @GradleTest + fun testCompileTestCouldAccessProduction(gradleVersion: GradleVersion) { + project("kotlin2JsProjectWithTests", gradleVersion) { + build("build") { + checkIrCompilationMessage() + + assertTasksExecuted( + ":compileKotlin2Js", + ":compileTestKotlin2Js" + ) + if (irBackend) { + assertFileInProjectExists("build/kotlin2js/main/default/manifest") + } else { + assertFileInProjectExists("build/kotlin2js/main/module.js") + } + assertFileInProjectExists("build/kotlin2js/test/module-tests.js") + } + } + } + + @DisplayName("test compilation can access internal symbols of main compilation") + @GradleTest + fun testCompilerTestAccessInternalProduction(gradleVersion: GradleVersion) { + project("kotlin2JsInternalTest", gradleVersion) { + build("runRhino") { + checkIrCompilationMessage() + } + } + } + + @DisplayName("works with custom source sets") + @GradleTest + fun testJsCustomSourceSet(gradleVersion: GradleVersion) { + project("kotlin2JsProjectWithCustomSourceset", gradleVersion) { + build("build") { + checkIrCompilationMessage() + + assertTasksExecuted( + ":compileKotlin2Js", + ":compileIntegrationTestKotlin2Js" + ) + + if (!irBackend) { + assertFileInProjectExists("build/kotlin2js/main/module.js") + } + assertFileInProjectExists("build/kotlin2js/integrationTest/module-inttests.js") + + val jarPath = projectPath.resolve("build/libs/kotlin2JsProjectWithCustomSourceset-inttests.jar") + assertFileExists(jarPath) + ZipFile(jarPath.toFile()).use { jar -> + assertEquals( + 1, jar.entries().asSequence().count { it.name == "module-inttests.js" }, + "The jar should contain an entry `module-inttests.js` with no duplicates" + ) + } + } + } + } + + + @DisplayName("source map is generated") + @GradleTest + @DisabledIf( + "org.jetbrains.kotlin.gradle.AbstractKotlin2JsGradlePluginIT#getIrBackend", + disabledReason = "Source maps are not supported in IR backend" + ) + fun testKotlinJsSourceMap(gradleVersion: GradleVersion) { + project("kotlin2JsNoOutputFileProject", gradleVersion) { + buildGradle.appendText( + """ + | + |compileKotlin2Js.kotlinOptions.sourceMap = true + |compileKotlin2Js.kotlinOptions.sourceMapPrefix = "prefixprefix/" + |compileKotlin2Js.kotlinOptions.outputFile = "${'$'}{buildDir}/kotlin2js/main/app.js" + """.trimMargin() + ) + + build("build") { + val mapFilePath = projectPath.resolve("build/kotlin2js/main/app.js.map") + assertFileExists(mapFilePath) + val sourceFilePath = "prefixprefix/src/main/kotlin/example/Dummy.kt" + assertFileContains(mapFilePath, "\"$sourceFilePath\"") + } + } + } + + @DisplayName("sources can be embedded into source map") + @GradleTest + @DisabledIf( + "org.jetbrains.kotlin.gradle.AbstractKotlin2JsGradlePluginIT#getIrBackend", + disabledReason = "Source maps are not supported in IR backend" + ) + fun testKotlinJsSourceMapInline(gradleVersion: GradleVersion) { + project("kotlin2JsProjectWithSourceMapInline", gradleVersion) { + build("build") { + val mapFilePath = subProject("app").kotlinClassesDir().resolve("app.js.map") + assertFileExists(mapFilePath) + assertFileContains( + mapFilePath, + "\"./src/main/kotlin/main.kt\"", + "\"./src/main/kotlin/foo.kt\"", + "\"fun main(args: Array) {", + "\"inline fun foo(): String {", ) } } } - @Test - fun testJarIncludesJsOutputSetExplicitly() { - val project = Project("kotlin2JsModuleKind") - - project.build(":jar") { - assertSuccessful() - checkIrCompilationMessage() - - assertTasksExecuted(":compileKotlin2Js") - val jarPath = "build/libs/kotlin2JsModuleKind.jar" - assertFileExists(jarPath) - val jar = ZipFile(fileInWorkingDir(jarPath)) - assertEquals( - 1, jar.entries().asSequence().count { it.name == "app.js" }, - "The jar should contain an entry `app.js` with no duplicates" - ) - } - } - - @Test - fun testModuleKind() { - val project = Project("kotlin2JsModuleKind") - - project.build("runRhino") { - assertSuccessful() - checkIrCompilationMessage() - } - } - - @Test - fun testDefaultOutputFile() { - val project = Project("kotlin2JsNoOutputFileProject") - - project.build("build") { - assertSuccessful() - checkIrCompilationMessage() - if (!irBackend) { - assertFileExists(kotlinClassesDir() + "kotlin2JsNoOutputFileProject.js") - } - assertFileExists(kotlinClassesDir(sourceSet = "test") + "kotlin2JsNoOutputFileProject_test.js") - } - } - - @Test - fun testCompileTestCouldAccessProduction() { - val project = Project("kotlin2JsProjectWithTests") - - project.build("build") { - assertSuccessful() - checkIrCompilationMessage() - - assertTasksExecuted( - ":compileKotlin2Js", - ":compileTestKotlin2Js" - ) - if (irBackend) { - assertFileExists("build/kotlin2js/main/default/manifest") - } else { - assertFileExists("build/kotlin2js/main/module.js") - } - assertFileExists("build/kotlin2js/test/module-tests.js") - } - } - - @Test - fun testCompilerTestAccessInternalProduction() { - val project = Project("kotlin2JsInternalTest") - - project.build("runRhino") { - assertSuccessful() - checkIrCompilationMessage() - } - } - - @Test - fun testJsCustomSourceSet() { - val project = Project("kotlin2JsProjectWithCustomSourceset") - - project.build("build") { - assertSuccessful() - checkIrCompilationMessage() - - assertTasksExecuted( - ":compileKotlin2Js", - ":compileIntegrationTestKotlin2Js" - ) - - if (!irBackend) { - assertFileExists("build/kotlin2js/main/module.js") - } - assertFileExists("build/kotlin2js/integrationTest/module-inttests.js") - - val jarPath = "build/libs/kotlin2JsProjectWithCustomSourceset-inttests.jar" - assertFileExists(jarPath) - val jar = ZipFile(fileInWorkingDir(jarPath)) - assertEquals( - 1, jar.entries().asSequence().count { it.name == "module-inttests.js" }, - "The jar should contain an entry `module-inttests.js` with no duplicates" - ) - } - } - - - @Test - fun testKotlinJsSourceMap() { - // TODO: Support source maps - assumeFalse(irBackend) - - val project = Project("kotlin2JsNoOutputFileProject") - - project.setupWorkingDir() - - project.projectDir.getFileByName("build.gradle").modify { - it + "\n" + - "compileKotlin2Js.kotlinOptions.sourceMap = true\n" + - "compileKotlin2Js.kotlinOptions.sourceMapPrefix = \"prefixprefix/\"\n" + - "compileKotlin2Js.kotlinOptions.outputFile = \"\${buildDir}/kotlin2js/main/app.js\"\n" - } - - project.build("build") { - assertSuccessful() - - val mapFilePath = "build/kotlin2js/main/app.js.map" - assertFileExists(mapFilePath) - val map = fileInWorkingDir(mapFilePath).readText() - - val sourceFilePath = "prefixprefix/src/main/kotlin/example/Dummy.kt" - assertTrue("Source map should contain reference to $sourceFilePath") { map.contains("\"$sourceFilePath\"") } - } - } - - @Test - fun testKotlinJsSourceMapInline() { - // TODO: Support source maps - assumeFalse(irBackend) - - val project = Project("kotlin2JsProjectWithSourceMapInline") - - project.build("build") { - assertSuccessful() - - val mapFilePath = kotlinClassesDir(subproject = "app") + "app.js.map" - assertFileExists(mapFilePath) - val map = fileInWorkingDir(mapFilePath).readText() - - assertTrue("Source map should contain reference to main.kt") { map.contains("\"./src/main/kotlin/main.kt\"") } - assertTrue("Source map should contain reference to foo.kt") { map.contains("\"./src/main/kotlin/foo.kt\"") } - assertTrue("Source map should contain source of main.kt") { map.contains("\"fun main(args: Array) {") } - assertTrue("Source map should contain source of foo.kt") { map.contains("\"inline fun foo(): String {") } - } - } - - @Test - fun testIncrementalCompilationDisabled() = Project("kotlin2JsICProject").run { - val options = defaultBuildOptions().run { + @DisplayName("non-incremental compilation works") + @GradleTest + fun testIncrementalCompilationDisabled(gradleVersion: GradleVersion) { + val jsOptions = defaultJsOptions.run { if (irBackend) copy(incrementalJsKlib = false) else copy(incrementalJs = false) } - - build("build", options = options) { - assertSuccessful() - checkIrCompilationMessage() - assertNotContains(USING_JS_INCREMENTAL_COMPILATION_MESSAGE) - } - } - - @Test - fun testNewKotlinJsPlugin() = with(Project("kotlin-js-plugin-project")) { - assumeFalse(irBackend) // TODO: Support IR version of kotlinx.html - setupWorkingDir() - gradleBuildScript().modify(::transformBuildScriptWithPluginsDsl) - gradleSettingsScript().modify(::transformBuildScriptWithPluginsDsl) - - build("publish", "runDceKotlin", "test", "runDceBenchmarkKotlin") { - assertSuccessful() - - assertTasksExecuted( - ":compileKotlinJs", ":compileTestKotlinJs", ":compileBenchmarkKotlinJs", - ":runDceKotlin", ":runDceBenchmarkKotlin" - ) - - val moduleDir = "build/repo/com/example/kotlin-js-plugin/1.0/" - - val publishedJar = fileInWorkingDir(moduleDir + "kotlin-js-plugin-1.0.jar") - ZipFile(publishedJar).use { zip -> - val entries = zip.entries().asSequence().map { it.name } - assertTrue { "kotlin-js-plugin.js" in entries } - } - - val publishedPom = fileInWorkingDir(moduleDir + "kotlin-js-plugin-1.0.pom") - val kotlinVersion = defaultBuildOptions().kotlinVersion - val pomText = publishedPom.readText().replace(Regex("\\s+"), "") - assertTrue { "kotlinx-html-js0.6.12compile" in pomText } - assertTrue { "kotlin-stdlib-js$kotlinVersionruntime" in pomText } - - assertFileExists(moduleDir + "kotlin-js-plugin-1.0-sources.jar") - - assertFileExists("build/js/node_modules/kotlin/kotlin.js") - assertFileExists("build/js/node_modules/kotlin/kotlin.js.map") - assertFileExists("build/js/node_modules/kotlin-test/kotlin-test.js") - assertFileExists("build/js/node_modules/kotlin-test/kotlin-test.js.map") - assertFileExists("build/js/node_modules/kotlin-test-js-runner/kotlin-test-nodejs-runner.js") - assertFileExists("build/js/node_modules/kotlin-test-js-runner/kotlin-test-nodejs-runner.js.map") - assertFileExists("build/js/node_modules/kotlin-js-plugin/kotlin/kotlin-js-plugin.js") - assertFileExists("build/js/node_modules/kotlin-js-plugin/kotlin/kotlin-js-plugin.js.map") - assertFileExists("build/js/node_modules/kotlin-js-plugin-test/kotlin/kotlin-js-plugin-test.js") - assertFileExists("build/js/node_modules/kotlin-js-plugin-test/kotlin/kotlin-js-plugin-test.js.map") - - // Gradle 6.6+ slightly changed format of xml test results - val testGradleVersion = GradleVersion.version(project.chooseWrapperVersionOrFinishTest()) - if (testGradleVersion < GradleVersion.version("6.6")) { - assertTestResults("testProject/kotlin-js-plugin-project/tests_pre6.6.xml", "nodeTest") - } else { - assertTestResults("testProject/kotlin-js-plugin-project/tests.xml", "nodeTest") + val options = defaultBuildOptions.copy(jsOptions = jsOptions) + project("kotlin2JsICProject", gradleVersion, buildOptions = options) { + build("build") { + checkIrCompilationMessage() + assertOutputDoesNotContain(USING_JS_INCREMENTAL_COMPILATION_MESSAGE) } } } - @Test - fun testYarnSetup() = with(Project("yarn-setup")) { - setupWorkingDir() - gradleBuildScript().modify(::transformBuildScriptWithPluginsDsl) - gradleSettingsScript().modify(::transformBuildScriptWithPluginsDsl) + @DisplayName("smoke test of org.jetbrains.kotlin.js plugin") + @GradleTest + @DisabledIf( + "org.jetbrains.kotlin.gradle.AbstractKotlin2JsGradlePluginIT#getIrBackend", + disabledReason = "kotlinx.html doesn't support IR" + ) + fun testNewKotlinJsPlugin(gradleVersion: GradleVersion) { + project("kotlin-js-plugin-project", gradleVersion) { + buildGradleKts.modify(::transformBuildScriptWithPluginsDsl) + settingsGradle.modify(::transformBuildScriptWithPluginsDsl) - build("yarnFolderRemove") { - assertSuccessful() - } + build("publish", "runDceKotlin", "test", "runDceBenchmarkKotlin") { + assertTasksExecuted( + ":compileKotlinJs", ":compileTestKotlinJs", ":compileBenchmarkKotlinJs", + ":runDceKotlin", ":runDceBenchmarkKotlin" + ) - build("kotlinYarnSetup", "yarnFolderCheck") { - assertSuccessful() + val moduleDir = projectPath.resolve("build/repo/com/example/kotlin-js-plugin/1.0/") - assertTasksExecuted( - ":kotlinYarnSetup", - ":yarnFolderCheck" - ) - } + val publishedJar = moduleDir.resolve("kotlin-js-plugin-1.0.jar") + ZipFile(publishedJar.toFile()).use { zip -> + val entries = zip.entries().asSequence().map { it.name } + assertTrue { "kotlin-js-plugin.js" in entries } + } - gradleBuildScript().appendText("\nyarn.version = \"1.9.3\"") + val publishedPom = moduleDir.resolve("kotlin-js-plugin-1.0.pom") + val kotlinVersion = defaultBuildOptions.kotlinVersion + val pomText = publishedPom.readText().replace(Regex("\\s+"), "") + assertTrue { "kotlinx-html-js0.6.12compile" in pomText } + assertTrue { "kotlin-stdlib-js$kotlinVersionruntime" in pomText } - build("yarnConcreteVersionFolderChecker") { - assertSuccessful() + assertFileExists(moduleDir.resolve("kotlin-js-plugin-1.0-sources.jar")) - assertTasksExecuted( - ":kotlinYarnSetup", - ":yarnConcreteVersionFolderChecker" - ) - } - } + assertFileInProjectExists("build/js/node_modules/kotlin/kotlin.js") + assertFileInProjectExists("build/js/node_modules/kotlin/kotlin.js.map") + assertFileInProjectExists("build/js/node_modules/kotlin-test/kotlin-test.js") + assertFileInProjectExists("build/js/node_modules/kotlin-test/kotlin-test.js.map") + assertFileInProjectExists("build/js/node_modules/kotlin-test-js-runner/kotlin-test-nodejs-runner.js") + assertFileInProjectExists("build/js/node_modules/kotlin-test-js-runner/kotlin-test-nodejs-runner.js.map") + assertFileInProjectExists("build/js/node_modules/kotlin-js-plugin/kotlin/kotlin-js-plugin.js") + assertFileInProjectExists("build/js/node_modules/kotlin-js-plugin/kotlin/kotlin-js-plugin.js.map") + assertFileInProjectExists("build/js/node_modules/kotlin-js-plugin-test/kotlin/kotlin-js-plugin-test.js") + assertFileInProjectExists("build/js/node_modules/kotlin-js-plugin-test/kotlin/kotlin-js-plugin-test.js.map") - @Test - fun testNpmDependencies() = with(Project("npm-dependencies")) { - setupWorkingDir() - gradleBuildScript().modify(::transformBuildScriptWithPluginsDsl) - - build("build") { - assertSuccessful() - assertFileExists("build/js/node_modules/file-dependency") - assertFileExists("build/js/node_modules/file-dependency-2") - assertFileExists("build/js/node_modules/file-dependency-3/index.js") - assertFileExists("build/js/node_modules/42/package.json") - } - } - - @Test - fun testPackageJsonWithPublicNpmDependencies() = with(Project("npm-dependencies")) { - setupWorkingDir() - gradleBuildScript().modify(::transformBuildScriptWithPluginsDsl) - - build("jsJar") { - assertSuccessful() - - val archive = fileInWorkingDir("build") - .resolve("libs") - .listFiles(FileFilter { - it.extension == if (irBackend) KLIB_TYPE else "jar" - })!! - .single() - - val zipFile = ZipFile(archive) - val packageJsonCandidates = zipFile.entries() - .asSequence() - .filter { it.name == NpmProject.PACKAGE_JSON } - .toList() - - assertTrue("Expected existence of package.json in archive") { - packageJsonCandidates.size == 1 - } - - zipFile.getInputStream(packageJsonCandidates.single()).use { - it.reader().use { - val packageJson = Gson().fromJson(it, PackageJson::class.java) - val devDep = "42" - val devDepVersion = "0.0.1" - assertTrue( - "Dev dependency \"$devDep\": \"$devDepVersion\" in package.json expected, but actual:\n" + - "${packageJson.devDependencies}" - ) { - val devDependencies = packageJson.devDependencies - devDependencies - .containsKey(devDep) && - devDependencies[devDep] == devDepVersion - } - - val dep = "@yworks/optimizer" - val depVersion = "1.0.6" - assertTrue( - "Dependency \"$dep\": \"$depVersion\" in package.json expected, but actual:\n" + - "${packageJson.dependencies}" - ) { - val dependencies = packageJson.dependencies - dependencies - .containsKey(dep) && - dependencies[dep] == depVersion - } - - val peerDep = "date-arithmetic" - val peerDepVersion = "4.1.0" - assertTrue( - "Peer dependency \"$peerDep\": \"$peerDepVersion\" in package.json expected, but actual:\n" + - "${packageJson.peerDependencies}" - ) { - val peerDependencies = packageJson.peerDependencies - peerDependencies - .containsKey(peerDep) && - peerDependencies[peerDep] == peerDepVersion - } + // Gradle 6.6+ slightly changed format of xml test results + if (gradleVersion < GradleVersion.version("6.6")) { + assertTestResults(projectPath.resolve("tests_pre6.6.xml"), "nodeTest") + } else { + assertTestResults(projectPath.resolve("tests.xml"), "nodeTest") } } } - - val newGradleBuildScript = gradleBuildScript() - .useLines { lines -> - lines - .filterNot { - it.contains("npm", ignoreCase = true) - } - .joinToString("\n") - } - gradleBuildScript().modify { - newGradleBuildScript - } - - build("jsJar") { - assertSuccessful() - val archive = fileInWorkingDir("build") - .resolve("libs") - .listFiles(FileFilter { - it.extension == if (irBackend) KLIB_TYPE else "jar" - })!! - .single() - - val packageJsonCandidates = ZipFile(archive).entries() - .asSequence() - .filter { it.name == NpmProject.PACKAGE_JSON } - .toList() - - assertTrue("Expected absence of package.json in ${archive.name}") { - packageJsonCandidates.isEmpty() - } - } } - @Test - fun testBrowserDistribution() = with(Project("kotlin-js-browser-project")) { - setupWorkingDir() - gradleBuildScript().modify(::transformBuildScriptWithPluginsDsl) - gradleSettingsScript().modify(::transformBuildScriptWithPluginsDsl) + @DisplayName("yarn is set up") + @GradleTest + fun testYarnSetup(gradleVersion: GradleVersion) { + project("yarn-setup", gradleVersion) { + build("yarnFolderRemove") - if (irBackend) { - gradleProperties().appendText(jsCompilerType(KotlinJsCompilerType.IR)) - } - - build("build") { - assertSuccessful() - - assertTasksExecuted(":app:browserProductionWebpack") - - assertFileExists("build/js/packages/kotlin-js-browser-base-js-ir") - assertFileExists("build/js/packages/kotlin-js-browser-base-js-legacy") - assertFileExists("build/js/packages/kotlin-js-browser-lib") - assertFileExists("build/js/packages/kotlin-js-browser-app") - - assertFileExists("app/build/distributions/app.js") - - if (!irBackend) { - assertTasksExecuted(":app:processDceKotlinJs") - - assertFileExists("build/js/packages/kotlin-js-browser-app/kotlin-dce") - - assertFileExists("build/js/packages/kotlin-js-browser-app/kotlin-dce/kotlin.js") - assertFileExists("build/js/packages/kotlin-js-browser-app/kotlin-dce/kotlin-js-browser-app.js") - assertFileExists("build/js/packages/kotlin-js-browser-app/kotlin-dce/kotlin-js-browser-lib.js") - assertFileExists("build/js/packages/kotlin-js-browser-app/kotlin-dce/kotlin-js-browser-base-js-legacy.js") - - assertFileExists("app/build/distributions/app.js.map") + build("kotlinYarnSetup", "yarnFolderCheck") { + assertTasksExecuted( + ":kotlinYarnSetup", + ":yarnFolderCheck" + ) } - } - build("clean", "browserDistribution") { - assertTasksExecuted( - ":app:processResources", - if (irBackend) ":app:browserProductionExecutableDistributeResources" else ":app:browserDistributeResources" + buildGradleKts.appendText( + """ + | + |yarn.version = "1.9.3" + """.trimMargin() ) - assertFileExists("app/build/distributions/index.html") + build("yarnConcreteVersionFolderChecker") { + assertTasksExecuted( + ":kotlinYarnSetup", + ":yarnConcreteVersionFolderChecker" + ) + } } } - @Test - fun testResolveJsProjectDependencyToMetadata() = with(Project("kotlin-js-browser-project")) { - setupWorkingDir() - gradleBuildScript().modify(::transformBuildScriptWithPluginsDsl) - gradleSettingsScript().modify(::transformBuildScriptWithPluginsDsl) - gradleProperties().appendText("kotlin.js.compiler=both") - - val compiler = if (irBackend) "IR" else "LEGACY" - - val pathPrefix = "metadataDependency: " - - val appBuild = projectDir.resolve("app/build.gradle.kts") - appBuild.modify { - it.replace("target {", "js($compiler) {") + @DisplayName("NPM dependencies are installed") + @GradleTest + fun testNpmDependencies(gradleVersion: GradleVersion) { + project("npm-dependencies", gradleVersion) { + build("build") { + assertDirectoryInProjectExists("build/js/node_modules/file-dependency") + assertDirectoryInProjectExists("build/js/node_modules/file-dependency-2") + assertFileInProjectExists("build/js/node_modules/file-dependency-3/index.js") + assertFileInProjectExists("build/js/node_modules/42/package.json") + } } - appBuild.appendText( - "\n" + """ - kotlin.sourceSets { - val main by getting { - dependencies { - // add these dependencies to check that they are resolved to metadata - api(project(":base")) - implementation(project(":base")) - compileOnly(project(":base")) - runtimeOnly(project(":base")) + } + + @DisplayName("public NPM dependencies are included into package.json") + @GradleTest + fun testPackageJsonWithPublicNpmDependencies(gradleVersion: GradleVersion) { + project("npm-dependencies", gradleVersion) { + build("jsJar") { + val archive = projectPath + .resolve("build/libs") + .allFilesWithExtension(if (irBackend) KLIB_TYPE else "jar") + .single() + + ZipFile(archive.toFile()).use { zipFile -> + val packageJsonCandidates = zipFile.entries() + .asSequence() + .filter { it.name == NpmProject.PACKAGE_JSON } + .toList() + + assertTrue("Expected existence of package.json in archive") { + packageJsonCandidates.size == 1 + } + + zipFile.getInputStream(packageJsonCandidates.single()).use { + it.reader().use { reader -> + val packageJson = Gson().fromJson(reader, PackageJson::class.java) + val devDep = "42" + val devDepVersion = "0.0.1" + assertTrue( + "Dev dependency \"$devDep\": \"$devDepVersion\" in package.json expected, but actual:\n" + + "${packageJson.devDependencies}" + ) { + val devDependencies = packageJson.devDependencies + devDependencies + .containsKey(devDep) && + devDependencies[devDep] == devDepVersion + } + + val dep = "@yworks/optimizer" + val depVersion = "1.0.6" + assertTrue( + "Dependency \"$dep\": \"$depVersion\" in package.json expected, but actual:\n" + + "${packageJson.dependencies}" + ) { + val dependencies = packageJson.dependencies + dependencies + .containsKey(dep) && + dependencies[dep] == depVersion + } + + val peerDep = "date-arithmetic" + val peerDepVersion = "4.1.0" + assertTrue( + "Peer dependency \"$peerDep\": \"$peerDepVersion\" in package.json expected, but actual:\n" + + "${packageJson.peerDependencies}" + ) { + val peerDependencies = packageJson.peerDependencies + peerDependencies + .containsKey(peerDep) && + peerDependencies[peerDep] == peerDepVersion } } } - - task("printMetadataFiles") { - doFirst { - listOf("api", "implementation", "compileOnly", "runtimeOnly").forEach { kind -> - val configuration = configurations.getByName(kind + "DependenciesMetadata") - configuration.files.forEach { println("$pathPrefix" + configuration.name + "->" + it.name) } + } + } + + buildGradleKts.modify { content -> + content + .lines() + .joinToString(separator = "\n") { + if (it.contains("npm", ignoreCase = true)) "" else it + } + } + + build("jsJar") { + val archive = Files.list(projectPath.resolve("build").resolve("libs")).use { files -> + files + .filter { it.extension == if (irBackend) KLIB_TYPE else "jar" } + .toList() + .single() + } + + ZipFile(archive.toFile()).use { zipFile -> + val packageJsonCandidates = zipFile.entries() + .asSequence() + .filter { it.name == NpmProject.PACKAGE_JSON } + .toList() + + assertTrue("Expected absence of package.json in ${archive.name}") { + packageJsonCandidates.isEmpty() + } + } + } + } + } + + @DisplayName("browser distribution is generated") + @GradleTest + fun testBrowserDistribution(gradleVersion: GradleVersion) { + project("kotlin-js-browser-project", gradleVersion) { + buildGradleKts.modify(::transformBuildScriptWithPluginsDsl) + + if (irBackend) { + gradleProperties.appendText(jsCompilerType(KotlinJsCompilerType.IR)) + } + + build("build") { + assertTasksExecuted(":app:browserProductionWebpack") + + assertDirectoryInProjectExists("build/js/packages/kotlin-js-browser-base-js-ir") + assertDirectoryInProjectExists("build/js/packages/kotlin-js-browser-base-js-legacy") + assertDirectoryInProjectExists("build/js/packages/kotlin-js-browser-lib") + assertDirectoryInProjectExists("build/js/packages/kotlin-js-browser-app") + + assertFileInProjectExists("app/build/distributions/app.js") + + if (!irBackend) { + assertTasksExecuted(":app:processDceKotlinJs") + + assertDirectoryInProjectExists("build/js/packages/kotlin-js-browser-app/kotlin-dce") + + assertFileInProjectExists("build/js/packages/kotlin-js-browser-app/kotlin-dce/kotlin.js") + assertFileInProjectExists("build/js/packages/kotlin-js-browser-app/kotlin-dce/kotlin-js-browser-app.js") + assertFileInProjectExists("build/js/packages/kotlin-js-browser-app/kotlin-dce/kotlin-js-browser-lib.js") + assertFileInProjectExists("build/js/packages/kotlin-js-browser-app/kotlin-dce/kotlin-js-browser-base-js-legacy.js") + + assertFileInProjectExists("app/build/distributions/app.js.map") + } + } + + build("clean", "browserDistribution") { + assertTasksExecuted( + ":app:processResources", + if (irBackend) ":app:browserProductionExecutableDistributeResources" else ":app:browserDistributeResources" + ) + + assertFileInProjectExists("app/build/distributions/index.html") + } + } + } + + @DisplayName("dependencies are resolved to metadata") + @GradleTest + fun testResolveJsProjectDependencyToMetadata(gradleVersion: GradleVersion) { + project("kotlin-js-browser-project", gradleVersion) { + buildGradleKts.modify(::transformBuildScriptWithPluginsDsl) + gradleProperties.appendText("kotlin.js.compiler=both") + + val compiler = if (irBackend) "IR" else "LEGACY" + + val pathPrefix = "metadataDependency: " + + val appBuild = projectPath.resolve("app/build.gradle.kts") + appBuild.modify { + it.replace("target {", "js($compiler) {") + } + appBuild.appendText( + """ + | + |kotlin.sourceSets { + | val main by getting { + | dependencies { + | // add these dependencies to check that they are resolved to metadata + | api(project(":base")) + | implementation(project(":base")) + | compileOnly(project(":base")) + | runtimeOnly(project(":base")) + | } + | } + | } + | + |task("printMetadataFiles") { + | doFirst { + | listOf("api", "implementation", "compileOnly", "runtimeOnly").forEach { kind -> + | val configuration = configurations.getByName(kind + "DependenciesMetadata") + | configuration.files.forEach { println("$pathPrefix" + configuration.name + "->" + it.name) } + | } + | } + |} + """.trimMargin() + ) + + val metadataDependencyRegex = "$pathPrefix(.*?)->(.*)".toRegex() + + build("printMetadataFiles") { + val suffix = if (irBackend) "ir" else "legacy" + val ext = if (irBackend) "klib" else "jar" + + val expectedFileName = "base-$suffix.$ext" + + val paths = metadataDependencyRegex + .findAll(output).map { it.groupValues[1] to it.groupValues[2] } + .filter { (_, f) -> "base" in f } + .toSet() + + assertEquals( + listOf("api", "implementation", "compileOnly", "runtimeOnly").map { + "$it$METADATA_CONFIGURATION_NAME_SUFFIX" to expectedFileName + }.toSet(), + paths + ) + } + } + } + + @DisplayName("no dependencies from other modules are declared") + @GradleTest + fun testNoUnintendedDevDependencies(gradleVersion: GradleVersion) { + project("kotlin-js-browser-project", gradleVersion) { + buildGradleKts.modify(::transformBuildScriptWithPluginsDsl) + + build("browserProductionWebpack") { + val appPackageJson = getSubprojectPackageJson(projectName = "kotlin-js-browser", subProject = "app") + val libPackageJson = getSubprojectPackageJson(projectName = "kotlin-js-browser", subProject = "lib") + + assertTrue("${appPackageJson.name} should contain css-loader") { + "css-loader" in appPackageJson.devDependencies + } + assertFalse("${libPackageJson.name} shouldn't contain css-loader") { + "css-loader" in libPackageJson.devDependencies + } + } + } + } + + @DisplayName("yarn resolutions works") + @GradleTest + fun testYarnResolution(gradleVersion: GradleVersion) { + project("kotlin-js-yarn-resolutions", gradleVersion) { + build("packageJson", "rootPackageJson", "kotlinNpmInstall") { + fun getPackageJson() = + projectPath.resolve("build/js") + .resolve(NpmProject.PACKAGE_JSON) + .let { + Gson().fromJson(it.readText(), PackageJson::class.java) } - } + + val name = "lodash" + val version = getPackageJson().resolutions?.get(name) + val requiredVersion = ">=1.0.0 <1.2.1 || >1.4.0 <2.0.0" + assertTrue("Root package.json must have resolution $name with version $requiredVersion, but $version found") { + version == requiredVersion } - """.trimIndent() - ) - val metadataDependencyRegex = "$pathPrefix(.*?)->(.*)".toRegex() - - build( - "printMetadataFiles", - options = defaultBuildOptions().copy(jsCompilerType = if (irBackend) KotlinJsCompilerType.IR else KotlinJsCompilerType.LEGACY) - ) { - assertSuccessful() - - val suffix = if (irBackend) "ir" else "legacy" - val ext = if (irBackend) "klib" else "jar" - - val expectedFileName = "base-$suffix.$ext" - - val paths = metadataDependencyRegex - .findAll(output).map { it.groupValues[1] to it.groupValues[2] } - .filter { (_, f) -> "base" in f } - .toSet() - - Assert.assertEquals( - listOf("api", "implementation", "compileOnly", "runtimeOnly").map { - "$it$METADATA_CONFIGURATION_NAME_SUFFIX" to expectedFileName - }.toSet(), - paths - ) - } - } - - @Test - fun testNoUnintendedDevDependencies() = with(Project("kotlin-js-browser-project")) { - setupWorkingDir() - gradleBuildScript().modify(::transformBuildScriptWithPluginsDsl) - gradleSettingsScript().modify(::transformBuildScriptWithPluginsDsl) - - build("browserProductionWebpack") { - assertSuccessful() - - val appPackageJson = getSubprojectPackageJson(projectName = "kotlin-js-browser", subProject = "app") - val libPackageJson = getSubprojectPackageJson(projectName = "kotlin-js-browser", subProject = "lib") - - assertTrue("${appPackageJson.name} should contain css-loader") { - "css-loader" in appPackageJson.devDependencies - } - assertFalse("${libPackageJson.name} shouldn't contain css-loader") { - "css-loader" in libPackageJson.devDependencies - } - } - } - - @Test - fun testYarnResolution() = with(Project("kotlin-js-yarn-resolutions")) { - setupWorkingDir() - gradleBuildScript().modify(::transformBuildScriptWithPluginsDsl) - - build("packageJson", "rootPackageJson", "kotlinNpmInstall") { - assertSuccessful() - - fun getPackageJson() = - fileInWorkingDir("build/js") - .resolve(NpmProject.PACKAGE_JSON) - .let { - Gson().fromJson(it.readText(), PackageJson::class.java) - } - - val name = "lodash" - val version = getPackageJson().resolutions?.get(name) - val requiredVersion = ">=1.0.0 <1.2.1 || >1.4.0 <2.0.0" - assertTrue("Root package.json must have resolution $name with version $requiredVersion, but $version found") { - version == requiredVersion - } - - val react = "react" - val reactVersion = getPackageJson().resolutions?.get(react) - val requiredReactVersion = "16.0.0" - assertTrue("Root package.json must have resolution $react with version $requiredReactVersion, but $reactVersion found") { - reactVersion == requiredReactVersion - } - } - } - - @Test - fun testDirectoryDependencyNotFailProjectResolution() { - with(Project("kotlin-js-nodejs-project")) { - setupWorkingDir() - gradleBuildScript().modify(::transformBuildScriptWithPluginsDsl) - gradleSettingsScript().modify(::transformBuildScriptWithPluginsDsl) - - gradleBuildScript().appendText( - """${"\n"} - dependencies { - implementation(files("${"$"}{projectDir}/custom")) - implementation(files("${"$"}{projectDir}/custom2")) + val react = "react" + val reactVersion = getPackageJson().resolutions?.get(react) + val requiredReactVersion = "16.0.0" + assertTrue("Root package.json must have resolution $react with version $requiredReactVersion, but $reactVersion found") { + reactVersion == requiredReactVersion } - """.trimIndent() + } + } + } + + @DisplayName("directories in dependencies doesn't break NPM resolution") + @GradleTest + fun testDirectoryDependencyNotFailProjectResolution(gradleVersion: GradleVersion) { + project("kotlin-js-nodejs-project", gradleVersion) { + buildGradle.appendText( + """ + | + |dependencies { + | implementation(files("${"$"}{projectDir}/custom")) + | implementation(files("${"$"}{projectDir}/custom2")) + |} + """.trimMargin() ) - build( - "packageJson" - ) { - assertSuccessful() + build("packageJson") + } + } + + @DisplayName("webpack-config-d directory created during the build is not ignored") + @GradleTest + fun testDynamicWebpackConfigD(gradleVersion: GradleVersion) { + project("js-dynamic-webpack-config-d", gradleVersion) { + build("build") { + assertDirectoryInProjectExists("build/js/packages/js-dynamic-webpack-config-d") + assertFileInProjectContains("build/js/packages/js-dynamic-webpack-config-d/webpack.config.js", "// hello from patch.js") } } } - @Test - fun testDynamicWebpackConfigD() { - with(Project("js-dynamic-webpack-config-d")) { - setupWorkingDir() - gradleBuildScript().modify(::transformBuildScriptWithPluginsDsl) - - build( - "build" - ) { - assertSuccessful() - assertFileExists("build/js/packages/js-dynamic-webpack-config-d") - assertFileContains("build/js/packages/js-dynamic-webpack-config-d/webpack.config.js", "// hello from patch.js") - } + @DisplayName("task configuration avoidance on browser project when help is requested") + @GradleTest + fun testBrowserNoTasksConfigurationOnHelp(gradleVersion: GradleVersion) { + project("kotlin-js-browser-project", gradleVersion) { + buildGradleKts.modify(::transformBuildScriptWithPluginsDsl) + buildGradleKts.appendText( + """ + | + |allprojects { + | tasks.configureEach { + | if (this is org.gradle.configuration.Help) return@configureEach + | throw GradleException("Task ${'$'}{path} shouldn't be configured") + | } + |} + """.trimMargin() + ) + build("help") } } - @Test - fun testBrowserNoTasksConfigurationOnHelp() = with(transformProjectWithPluginsDsl("kotlin-js-browser-project")) { - gradleBuildScript().appendText( - "\n" + """ - allprojects { - tasks.configureEach { - if (this is org.gradle.configuration.Help) return@configureEach - throw GradleException("Task ${'$'}{path} shouldn't be configured") - } - } - """.trimIndent() - ) - build { - assertSuccessful() + @DisplayName("task configuration avoidance on nodejs project when help is requested") + @GradleTest + fun testNodeJsNoTasksConfigurationOnHelp(gradleVersion: GradleVersion) { + project("kotlin-js-nodejs-project", gradleVersion) { + buildGradle.appendText( + """ + | + |allprojects { + | tasks.configureEach { + | if (it instanceof org.gradle.configuration.Help) return + | throw new GradleException("Task ${'$'}{path} shouldn't be configured") + | } + |} + """.trimMargin() + ) + build("help") } } - @Test - fun testNodeJsNoTasksConfigurationOnHelp() = with(transformProjectWithPluginsDsl("kotlin-js-nodejs-project")) { - gradleBuildScript().appendText( - "\n" + """ - allprojects { - tasks.configureEach { - if (it instanceof org.gradle.configuration.Help) return - throw new GradleException("Task ${'$'}{path} shouldn't be configured") - } - } - """.trimIndent() - ) - build { - assertSuccessful() + @DisplayName("webpack configuration is valid") + @GradleTest + fun testWebpackConfig(gradleVersion: GradleVersion) { + project("kotlin-js-test-webpack-config", gradleVersion) { + build("browserDevelopmentWebpack") + + build("checkConfigDevelopmentWebpack") + + build("checkConfigProductionWebpack") + + build("checkConfigDevelopmentRun") + + build("checkConfigProductionRun") } } - private fun CompiledProject.getSubprojectPackageJson(subProject: String, projectName: String? = null) = - fileInWorkingDir("build/js/packages/${projectName ?: project.projectName}-$subProject") + private fun TestProject.getSubprojectPackageJson(subProject: String, projectName: String? = null) = + projectPath.resolve("build/js/packages/${projectName ?: projectName}-$subProject") .resolve(NpmProject.PACKAGE_JSON) .let { Gson().fromJson(it.readText(), PackageJson::class.java) } - - @Test - fun testWebpackConfig() { - with(Project("kotlin-js-test-webpack-config")) { - setupWorkingDir() - gradleBuildScript().modify(::transformBuildScriptWithPluginsDsl) - - build( - "browserDevelopmentWebpack" - ) { - assertSuccessful() - } - - build( - "checkConfigDevelopmentWebpack", - ) { - assertSuccessful() - } - - build( - "checkConfigProductionWebpack", - ) { - assertSuccessful() - } - - build( - "checkConfigDevelopmentRun", - ) { - assertSuccessful() - } - - build( - "checkConfigProductionRun", - ) { - assertSuccessful() - } - } - } } -class GeneralKotlin2JsGradlePluginIT : BaseGradleIT() { - @Test - fun testJsBothModeWithTests() = with(Project("kotlin-js-both-mode-with-tests")) { - setupWorkingDir() - gradleBuildScript().modify(::transformBuildScriptWithPluginsDsl) - - build("build") { - assertSuccessful() - assertNoWarnings() - } - } - - @Test - fun testNodeJsAndYarnDownload() = with(transformProjectWithPluginsDsl("cleanTask")) { - - build("checkDownloadedFolder") { - assertSuccessful() - } - - build("checkIfLastModifiedNotNow", "--rerun-tasks") { - assertSuccessful() - } - } - - @Test - fun testIncrementalDceDevModeOnExternalDependency() = with(transformProjectWithPluginsDsl("kotlin-js-browser-project")) { - val libBuildscript = projectDir.resolve("lib/build.gradle.kts") - - build(":base:assemble") { - assertSuccessful() - fileInWorkingDir("base/build/libs/base-legacy.jar").copyTo(fileInWorkingDir("base.1.jar")) - } - - projectFile("Base.kt").appendText("\nfun bestRandom() = 4") - - build(":base:assemble") { - assertSuccessful() - fileInWorkingDir("base/build/libs/base-legacy.jar").copyTo(fileInWorkingDir("base.2.jar")) - } - - libBuildscript.modify { - it.replace("implementation(project(\":base\"))", "implementation(files(\"../base.1.jar\"))") - } - libBuildscript.appendText(""" - kotlin.js().browser { - dceTask { - dceOptions.devMode = true - } +@SimpleGradlePluginTests +class GeneralKotlin2JsGradlePluginIT : KGPBaseTest() { + @DisplayName("js with both backends mode builds successfully") + @GradleTest + fun testJsBothModeWithTests(gradleVersion: GradleVersion) { + project("kotlin-js-both-mode-with-tests", gradleVersion) { + build("build") { + assertNoBuildWarnings() } - """.trimIndent()) - - val baseDceFile = "build/js/packages/kotlin-js-browser-lib/kotlin-dce/kotlin-js-browser-base-js-legacy.js" - - build(":lib:assemble") { - assertSuccessful() - assertFileExists(baseDceFile) - assert(!fileInWorkingDir(baseDceFile).readText().contains("bestRandom")) } + } - libBuildscript.modify { - it.replace("../base.1.jar", "../base.2.jar") - } + @DisplayName("nodejs up-to-date check works") + @GradleTest + fun testNodeJsAndYarnDownload(gradleVersion: GradleVersion) { + project("cleanTask", gradleVersion) { + build("checkDownloadedFolder") - build(":lib:assemble") { - assertSuccessful() - assertFileExists(baseDceFile) - assert(fileInWorkingDir(baseDceFile).readText().contains("bestRandom")) + build("checkIfLastModifiedNotNow", "--rerun-tasks") } } } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinSpecificDependenciesIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinSpecificDependenciesIT.kt index 210edf35cb5..84aa078f0aa 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinSpecificDependenciesIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KotlinSpecificDependenciesIT.kt @@ -31,6 +31,7 @@ class KotlinSpecificDependenciesIT : BaseGradleIT() { } private fun jsProject() = Project("kotlin-js-plugin-project").apply { + // TODO: remove settings.gradle from test project after migrating to the new test DSL setupWorkingDir() gradleBuildScript().modify { it.lines().filter { "html" !in it }.joinToString("\n") } projectFile("Main.kt").modify { "fun f() = listOf(1, 2, 3).joinToString()" } diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/SimpleKotlinGradleIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/SimpleKotlinGradleIT.kt index ee65354ea56..694deeb2b91 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/SimpleKotlinGradleIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/SimpleKotlinGradleIT.kt @@ -127,7 +127,7 @@ class SimpleKotlinGradleIT : KGPBaseTest() { ) build("build") { - assertDirectoryExists("$customBuildDirName/classes") + assertDirectoryInProjectExists("$customBuildDirName/classes") assertFileNotExists("build") } } diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/tasks/CleanDataTaskIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/tasks/CleanDataTaskIT.kt index 7350fdcc9ce..bca860ead8a 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/tasks/CleanDataTaskIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/tasks/CleanDataTaskIT.kt @@ -5,20 +5,22 @@ package org.jetbrains.kotlin.gradle.tasks +import org.gradle.util.GradleVersion import org.jetbrains.kotlin.gradle.BaseGradleIT +import org.jetbrains.kotlin.gradle.testbase.* import org.jetbrains.kotlin.gradle.transformProjectWithPluginsDsl import org.junit.Test +import org.junit.jupiter.api.DisplayName -class CleanDataTaskIT : BaseGradleIT() { +@SimpleGradlePluginTests +class CleanDataTaskIT : KGPBaseTest() { - @Test - fun testDownloadedFolderDeletion() { - val project = transformProjectWithPluginsDsl("cleanTask") - - project.build("testCleanTask") { - assertSuccessful() + @DisplayName("nodejs is deleted from Gradle user home") + @GradleTest + fun testDownloadedFolderDeletion(gradleVersion: GradleVersion) { + project("cleanTask", gradleVersion) { + build("testCleanTask") } - } } diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/BuildOptions.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/BuildOptions.kt index 1333abcf3e7..8d772c75ecd 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/BuildOptions.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/BuildOptions.kt @@ -9,6 +9,7 @@ import org.gradle.api.logging.LogLevel import org.gradle.api.logging.configuration.WarningMode import org.gradle.util.GradleVersion import org.jetbrains.kotlin.gradle.BaseGradleIT +import org.jetbrains.kotlin.gradle.plugin.KotlinJsCompilerType data class BuildOptions( val logLevel: LogLevel = LogLevel.INFO, @@ -23,7 +24,8 @@ data class BuildOptions( val fileSystemWatchEnabled: Boolean = false, val buildCacheEnabled: Boolean = false, val kaptOptions: KaptOptions? = null, - val androidVersion: String? = null + val androidVersion: String? = null, + val jsOptions: JsOptions? = null, ) { data class KaptOptions( val verbose: Boolean = false, @@ -33,6 +35,13 @@ data class BuildOptions( val classLoadersCacheSize: Int? = null ) + data class JsOptions( + val useIrBackend: Boolean? = null, + val jsCompilerType: KotlinJsCompilerType? = null, + val incrementalJs: Boolean? = null, + val incrementalJsKlib: Boolean? = null, + ) + fun toArguments( gradleVersion: GradleVersion ): List { @@ -90,6 +99,13 @@ 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.useIrBackend?.let { arguments.add("-Pkotlin.js.useIrBackend=$it") } + jsOptions.jsCompilerType?.let { arguments.add("-Pkotlin.js.compiler=$it") } + } + if (androidVersion != null) { arguments.add("-Pandroid_tools_version=${androidVersion}") } diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/compilationAssertions.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/compilationAssertions.kt new file mode 100644 index 00000000000..26f568d4437 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/compilationAssertions.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2010-2021 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.testbase + +import java.nio.file.Path +import java.nio.file.Paths +import kotlin.io.path.relativeTo +import org.gradle.api.logging.LogLevel + +private val kotlinSrcRegex by lazy { Regex("\\[KOTLIN\\] compile iteration: ([^\\r\\n]*)") } + +/** + * Extracts list of compiled .kt files from task output + * + * Note: log level of output should be set to [LogLevel.DEBUG] + */ +fun extractKotlinCompiledSources(projectDirectory: Path, output: String) = + kotlinSrcRegex.findAll(output) + .asIterable() + .flatMap { result -> + result.groups[1]!!.value.split(", ") + .map { source -> projectDirectory.resolve(source).normalize() } + } + +private val javaSrcRegex by lazy { Regex("\\[DEBUG\\] \\[[^\\]]*JavaCompiler\\] Compiler arguments: ([^\\r\\n]*)") } + +/** + * Extracts list of compiled .java files from task output + * + * Note: log level of output should be set to [LogLevel.DEBUG] + */ +fun extractJavaCompiledSources(output: String): List = + javaSrcRegex.findAll(output).asIterable().flatMap { + it.groups[1]!!.value + .split(" ") + .filter { source -> source.endsWith(".java", ignoreCase = true) } + .map { source -> Paths.get(source).normalize() } + } + +/** + * Asserts all the .kt files from [expectedSources] and only they are compiled + * + * Note: log level of output should be set to [LogLevel.DEBUG] + */ +fun GradleProject.assertCompiledKotlinSources(expectedSources: Iterable, output: String) { + val actualSources = extractKotlinCompiledSources(projectPath, output).map { + it.relativeTo(projectPath) + } + assertSameFiles(expectedSources, actualSources, "Compiled Kotlin files differ:\n") +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/fileAssertions.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/fileAssertions.kt index 054c1aa5f00..8a6ab2806e7 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/fileAssertions.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/fileAssertions.kt @@ -7,6 +7,10 @@ package org.jetbrains.kotlin.gradle.testbase import java.nio.file.Files import java.nio.file.Path +import kotlin.io.path.exists +import kotlin.io.path.isDirectory +import kotlin.io.path.readText +import kotlin.test.assertEquals /** * Asserts file under [file] path exists and is a regular file. @@ -47,16 +51,103 @@ fun GradleProject.assertFileNotExists( /** * Asserts directory under [pathToDir] relative to the test project exists and is a directory. */ -fun GradleProject.assertDirectoryExists( +fun GradleProject.assertDirectoryInProjectExists( pathToDir: String +) = assertDirectoryExists(projectPath.resolve(pathToDir)) + +/** + * Asserts directory under [file] exists and is a directory. + */ +fun GradleProject.assertDirectoryExists( + dirPath: Path +) = assertDirectoriesExist(dirPath) + +fun GradleProject.assertDirectoriesExist( + vararg dirPaths: Path ) { - val dirPath = projectPath.resolve(pathToDir) + val (exist, notExist) = dirPaths.partition { it.exists() } + val notDirectories = exist.filterNot { it.isDirectory() } - assert(Files.exists(dirPath)) { - "Directory '${dirPath}' does not exist!" + assert(notExist.isEmpty() && notDirectories.isEmpty()) { + buildString { + if (notExist.isNotEmpty()) { + appendLine("Following directories does not exist:") + appendLine(notExist.joinToString(separator = "\n")) + } + if (notDirectories.isNotEmpty()) { + appendLine("Following files should be directories:") + appendLine(notExist.joinToString(separator = "\n")) + } + } } +} - assert(Files.isDirectory(dirPath)) { - "'${dirPath}' is not a directory!" +/** + * Asserts file under [pathToFile] relative to the test project exists and contains all the lines from [expectedLines] + */ +fun GradleProject.assertFileInProjectContains( + pathToFile: String, + vararg expectedText: String +) { + assertFileContains(projectPath.resolve(pathToFile), *expectedText) +} + +/** + * Asserts file under [pathToFile] relative to the test project exists and does not contain any line from [unexpectedText] + */ +fun GradleProject.assertFileInProjectDoesNotContain( + pathToFile: String, + vararg unexpectedText: String +) { + assertFileDoesNotContain(projectPath.resolve(pathToFile), *unexpectedText) +} + +/** + * Asserts file under [file] exists and contains all the lines from [expectedText] + */ +fun GradleProject.assertFileContains( + file: Path, + vararg expectedText: String +) { + assertFileExists(file) + val text = file.readText() + val textNotInTheFile = expectedText.filterNot { text.contains(it) } + assert(textNotInTheFile.isEmpty()) { + """ + |$file does not contain: + |${textNotInTheFile.joinToString(separator = "\n")} + | + |actual file content: + |$text" + | + """.trimMargin() } +} + +/** + * Asserts file under [file] exists and does not contain any line from [unexpectedText] + */ +fun GradleProject.assertFileDoesNotContain( + file: Path, + vararg unexpectedText: String +) { + assertFileExists(file) + val text = file.readText() + val textInTheFile = unexpectedText.filter { text.contains(it) } + assert(textInTheFile.isEmpty()) { + """ + |$file contains lines which it should not contain: + |${textInTheFile.joinToString(separator = "\n")} + | + |actual file content: + |$text" + | + """.trimMargin() + } +} + +fun GradleProject.assertSameFiles(expected: Iterable, actual: Iterable, messagePrefix: String) { + val expectedSet = expected.map { it.toString().normalizePath() }.toSortedSet().joinToString("\n") + val actualSet = actual.map { it.toString().normalizePath() }.toSortedSet().joinToString("\n") + assertEquals(expectedSet, actualSet, messagePrefix) } \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/outputAssertions.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/outputAssertions.kt index b2a3dc00cd3..3a85ec8be46 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/outputAssertions.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/outputAssertions.kt @@ -82,7 +82,7 @@ fun BuildResult.assertOutputDoesNotContain( ) { assert(!output.contains(regexToCheck)) { printBuildOutput() - + val matchedStrings = regexToCheck .findAll(output) .map { it.value } @@ -189,4 +189,4 @@ fun BuildResult.assertIncrementalCompilation( |${incrementalCompilationOptions.joinToString(separator = "\n")} """.trimMargin() } -} \ No newline at end of file +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/pathHelpers.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/pathHelpers.kt index a7730ed6384..25b08a40ec5 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/pathHelpers.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/pathHelpers.kt @@ -7,7 +7,10 @@ package org.jetbrains.kotlin.gradle.testbase import java.nio.file.Files import java.nio.file.Path +import kotlin.io.path.extension +import kotlin.io.path.relativeTo import kotlin.streams.asSequence +import kotlin.streams.toList /** * Find the file with given [name] in current [Path]. @@ -15,9 +18,7 @@ import kotlin.streams.asSequence * @return `null` if file is absent in current [Path] */ internal fun Path.findInPath(name: String): Path? = Files - .walk(this) - .asSequence() - .find { it.fileName.toString() == name } + .walk(this).use { stream -> stream.asSequence().find { it.fileName.toString() == name } } /** * Create a temporary directory that will be cleaned up on normal JVM termination, but will be left on non-zero exit status. @@ -27,3 +28,18 @@ internal fun Path.findInPath(name: String): Path? = Files internal fun createTempDir(prefix: String): Path = Files .createTempDirectory(prefix) .apply { toFile().deleteOnExit() } + +/** + * Returns list of all files whose name ends with [ext] extension. The comparison is case-insensitive. + */ +internal fun Path.allFilesWithExtension(ext: String): List = + Files.walk(this).use { stream -> stream.filter { it.extension.equals(ext, ignoreCase = true) }.toList() } + +internal val Path.allKotlinFiles + get() = allFilesWithExtension("kt") + +internal fun Iterable.relativizeTo(basePath: Path): Iterable = map { + it.relativeTo(basePath) +} + +internal fun String.normalizePath() = replace("\\", "/") \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/projectSetupDefaults.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/projectSetupDefaults.kt index aac6bc5f29b..45b7473c76b 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/projectSetupDefaults.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/projectSetupDefaults.kt @@ -28,16 +28,21 @@ internal val DEFAULT_GROOVY_SETTINGS_FILE = resolutionStrategy { eachPlugin { - if (requested.id.id == "com.android.application" || - requested.id.id == "com.android.library" || - requested.id.id == "com.android.test" || - requested.id.id == "com.android.dynamic-feature" || - requested.id.id == "com.android.asset-pack" || - requested.id.id == "com.android.asset-pack-bundle" || - requested.id.id == "com.android.lint" || - requested.id.id == "com.android.instantapp" || - requested.id.id == "com.android.feature") { - useModule("com.android.tools.build:gradle:${'$'}android_tools_version") + switch (requested.id.id) { + case "com.android.application": + case "com.android.library": + case "com.android.test": + case "com.android.dynamic-feature": + case "com.android.asset-pack": + case "com.android.asset-pack-bundle": + case "com.android.lint": + case "com.android.instantapp": + case "com.android.feature": + useModule("com.android.tools.build:gradle:${'$'}android_tools_version") + break + case "kotlin-dce-js": + useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:${'$'}kotlin_version") + break } } } diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testAssertions.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testAssertions.kt new file mode 100644 index 00000000000..759ce9f713a --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testAssertions.kt @@ -0,0 +1,78 @@ +/* + * Copyright 2010-2021 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.testbase + +import org.jdom.Element +import org.jdom.input.SAXBuilder +import org.jdom.output.Format +import org.jdom.output.XMLOutputter +import org.jetbrains.kotlin.test.util.trimTrailingWhitespaces +import java.nio.file.Files +import java.nio.file.Path +import kotlin.io.path.* +import kotlin.streams.asSequence +import kotlin.streams.toList +import kotlin.test.assertEquals + +fun GradleProject.assertTestResults(expectedTestReport: Path, vararg testReportNames: String) { + val testReportDirs = testReportNames.map { projectPath.resolve("build/test-results/$it") } + + assertDirectoriesExist(*testReportDirs.toTypedArray()) + + val actualTestResults = readAndCleanupTestResults(testReportDirs, projectPath) + val expectedTestResults = prettyPrintXml(expectedTestReport.readText()) + + assertEquals(expectedTestResults, actualTestResults) +} + +internal fun readAndCleanupTestResults(testReportDirs: List, projectPath: Path): String { + val files = testReportDirs + .flatMap { + it.allFilesWithExtension("xml") + } + .sortedBy { + // let containing test suite be first + it.name.replace(".xml", ".A.xml") + } + + val xmlString = buildString { + appendLine("") + appendLine("") + files.forEach { file -> + appendLine( + file.readText() + .trimTrailingWhitespaces() + .replace(projectPath.absolutePathString(), "/\$PROJECT_DIR$") + .replace(projectPath.name, "\$PROJECT_NAME$") + .replace("\n", "") + ) + } + appendLine("") + } + + val doc = SAXBuilder().build(xmlString.reader()) + val skipAttrs = setOf("timestamp", "hostname", "time", "message") + val skipContentsOf = setOf("failure") + + fun cleanup(e: Element) { + if (e.name in skipContentsOf) e.text = "..." + e.attributes.forEach { + if (it.name in skipAttrs) { + it.value = "..." + } + } + + e.children.forEach { + cleanup(it) + } + } + + cleanup(doc.rootElement) + return XMLOutputter(Format.getPrettyFormat()).outputString(doc) +} + +internal fun prettyPrintXml(uglyXml: String): String = + XMLOutputter(Format.getPrettyFormat()).outputString(SAXBuilder().build(uglyXml.reader())) \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testDsl.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testDsl.kt index a60572ca9a1..fbf0594f61b 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testDsl.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testDsl.kt @@ -321,7 +321,7 @@ private fun BuildResult.printBuildScanUrl() { private fun TestProject.setupNonDefaultJdk(pathToJdk: File) { gradleProperties.modify { """ - |org.gradle.java.home=${pathToJdk.absolutePath.replace('\\', '/')} + |org.gradle.java.home=${pathToJdk.absolutePath.normalizePath()} | |$it """.trimMargin() @@ -335,7 +335,7 @@ internal fun Path.enableAndroidSdk() { .also { if (!it.exists()) it.createFile() } .appendText( """ - sdk.dir=${androidSdk.absolutePath.replace('\\', '/')} + sdk.dir=${androidSdk.absolutePath.normalizePath()} """.trimIndent() ) acceptAndroidSdkLicenses(androidSdk) diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/util/fileUtil.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/util/fileUtil.kt index 363b2a36bcf..9bc97fbe76f 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/util/fileUtil.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/util/fileUtil.kt @@ -7,6 +7,8 @@ package org.jetbrains.kotlin.gradle.util import java.io.File import java.nio.file.Files +import java.nio.file.Path +import kotlin.io.path.relativeTo fun File.getFileByName(name: String): File = findFileByName(name) ?: throw AssertionError("Could not find file with name '$name' in $this") diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/cleanTask/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/cleanTask/build.gradle index 3d4dbe82c39..927a1db8138 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/cleanTask/build.gradle +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/cleanTask/build.gradle @@ -5,7 +5,7 @@ import java.time.* import java.util.concurrent.* plugins { - id "org.jetbrains.kotlin.js" version "" + id "org.jetbrains.kotlin.js" } String nodeJsVersion diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/cleanTask/settings.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/cleanTask/settings.gradle deleted file mode 100644 index d0627d7c827..00000000000 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/cleanTask/settings.gradle +++ /dev/null @@ -1,9 +0,0 @@ -pluginManagement { - repositories { - maven { url '' } - gradlePluginPortal() - mavenCentral() - } -} - -rootProject.name = 'cleanTask' \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/js-composite-build/app/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/js-composite-build/build.gradle.kts similarity index 100% rename from libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/js-composite-build/app/build.gradle.kts rename to libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/js-composite-build/build.gradle.kts diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/js-composite-build/lib/gradle.properties b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/js-composite-build/lib/gradle.properties new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/js-composite-build/lib/settings.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/js-composite-build/lib/settings.gradle similarity index 81% rename from libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/js-composite-build/lib/settings.gradle.kts rename to libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/js-composite-build/lib/settings.gradle index f9bb2765477..431ec5d5051 100755 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/js-composite-build/lib/settings.gradle.kts +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/js-composite-build/lib/settings.gradle @@ -1,5 +1,3 @@ -rootProject.name = "lib2" - pluginManagement { repositories { gradlePluginPortal() @@ -8,4 +6,6 @@ pluginManagement { mavenCentral() } -} \ No newline at end of file +} + +rootProject.name = "lib2" \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/js-composite-build/app/settings.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/js-composite-build/settings.gradle similarity index 100% rename from libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/js-composite-build/app/settings.gradle.kts rename to libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/js-composite-build/settings.gradle diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/js-composite-build/app/src/main/kotlin/Main.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/js-composite-build/src/main/kotlin/Main.kt similarity index 100% rename from libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/js-composite-build/app/src/main/kotlin/Main.kt rename to libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/js-composite-build/src/main/kotlin/Main.kt diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/js-composite-build/app/src/test/kotlin/SimpleTest.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/js-composite-build/src/test/kotlin/SimpleTest.kt similarity index 100% rename from libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/js-composite-build/app/src/test/kotlin/SimpleTest.kt rename to libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/js-composite-build/src/test/kotlin/SimpleTest.kt diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/js-dynamic-webpack-config-d/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/js-dynamic-webpack-config-d/build.gradle.kts index 5d48cd6b30b..fb790f69fe0 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/js-dynamic-webpack-config-d/build.gradle.kts +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/js-dynamic-webpack-config-d/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - kotlin("js") version "" + kotlin("js") } group = "com.example" diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/js-dynamic-webpack-config-d/settings.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/js-dynamic-webpack-config-d/settings.gradle.kts deleted file mode 100644 index 4501976308c..00000000000 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/js-dynamic-webpack-config-d/settings.gradle.kts +++ /dev/null @@ -1,8 +0,0 @@ -pluginManagement { - repositories { - mavenLocal() - gradlePluginPortal() - } -} - -rootProject.name = "js-dynamic-webpack-config-d" \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-both-mode-with-tests/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-both-mode-with-tests/build.gradle.kts index 2ece8609a85..80484752885 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-both-mode-with-tests/build.gradle.kts +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-both-mode-with-tests/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - kotlin("js").version("") + kotlin("js") } group = "com.example" diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-both-mode-with-tests/settings.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-both-mode-with-tests/settings.gradle.kts deleted file mode 100644 index 395516d756e..00000000000 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-both-mode-with-tests/settings.gradle.kts +++ /dev/null @@ -1,8 +0,0 @@ -pluginManagement { - repositories { - mavenLocal() - gradlePluginPortal() - } -} - -rootProject.name = "kotlin-js-both-mode-with-tests" \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-browser-project/settings.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-browser-project/settings.gradle similarity index 100% rename from libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-browser-project/settings.gradle.kts rename to libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-browser-project/settings.gradle diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-dependency-with-js-files/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-dependency-with-js-files/build.gradle.kts index 8a508556045..110466f9f6b 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-dependency-with-js-files/build.gradle.kts +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-dependency-with-js-files/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - kotlin("js") version "" + kotlin("js") } group = "org.example" diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-dependency-with-js-files/settings.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-dependency-with-js-files/settings.gradle.kts deleted file mode 100644 index c9052c50d60..00000000000 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-dependency-with-js-files/settings.gradle.kts +++ /dev/null @@ -1,8 +0,0 @@ -pluginManagement { - repositories { - mavenLocal() - gradlePluginPortal() - } -} - -rootProject.name = "kotlin-js-dependency-with-js-files" \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-nodejs-project/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-nodejs-project/build.gradle index 5da9fe0c507..b5f207c5c0c 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-nodejs-project/build.gradle +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-nodejs-project/build.gradle @@ -1,5 +1,5 @@ plugins { - id("org.jetbrains.kotlin.js") version "" + id("org.jetbrains.kotlin.js") } dependencies { diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-nodejs-project/settings.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-nodejs-project/settings.gradle deleted file mode 100644 index b39d9d0e83b..00000000000 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-nodejs-project/settings.gradle +++ /dev/null @@ -1,8 +0,0 @@ -pluginManagement { - repositories { - mavenLocal() - gradlePluginPortal() - } -} - -rootProject.name = "kotlin-js-nodejs" \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-plugin-project/settings.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-plugin-project/settings.gradle similarity index 58% rename from libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-plugin-project/settings.gradle.kts rename to libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-plugin-project/settings.gradle index b6cdbb8e386..ad4f3a6409f 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-plugin-project/settings.gradle.kts +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-plugin-project/settings.gradle @@ -1,8 +1,8 @@ pluginManagement { resolutionStrategy { eachPlugin{ - when (requested.id.id) { - "kotlin-dce-js" -> useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:") + if (requested.id.id == "kotlin-dce-js") { + useModule("org.jetbrains.kotlin:kotlin-gradle-plugin:") } } } diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-test-webpack-config/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-test-webpack-config/build.gradle.kts index ab3babad3d4..b0ad3c6e8b5 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-test-webpack-config/build.gradle.kts +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-test-webpack-config/build.gradle.kts @@ -1,7 +1,7 @@ import org.jetbrains.kotlin.gradle.targets.js.npm.npmProject plugins { - kotlin("js") version "" + kotlin("js") } dependencies { diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-test-webpack-config/settings.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-test-webpack-config/settings.gradle.kts deleted file mode 100644 index dcb66c03adb..00000000000 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-test-webpack-config/settings.gradle.kts +++ /dev/null @@ -1,8 +0,0 @@ -pluginManagement { - repositories { - mavenLocal() - gradlePluginPortal() - } -} - -rootProject.name = "kotlin-js-test-webpack-config" \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-yarn-resolutions/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-yarn-resolutions/build.gradle.kts index 506ce3c5285..b84da5c562e 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-yarn-resolutions/build.gradle.kts +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-yarn-resolutions/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - kotlin("js").version("") + kotlin("js") } group = "com.example" diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-yarn-resolutions/settings.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-yarn-resolutions/settings.gradle new file mode 100644 index 00000000000..657e14a0917 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-yarn-resolutions/settings.gradle @@ -0,0 +1,4 @@ +rootProject.name = "kotlin-js-yarn-resolutions" + +include("base") +include("lib") \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-yarn-resolutions/settings.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-yarn-resolutions/settings.gradle.kts deleted file mode 100644 index f2be43ab08f..00000000000 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin-js-yarn-resolutions/settings.gradle.kts +++ /dev/null @@ -1,11 +0,0 @@ -pluginManagement { - repositories { - mavenLocal() - gradlePluginPortal() - } -} - -rootProject.name = "kotlin-js-yarn-resolutions" - -include("base") -include("lib") \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/npm-dependencies/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/npm-dependencies/build.gradle.kts index 6d2581fd72b..c2f197e055c 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/npm-dependencies/build.gradle.kts +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/npm-dependencies/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - kotlin("js") version "" + kotlin("js") } group = "com.example" diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/npm-dependencies/settings.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/npm-dependencies/settings.gradle.kts deleted file mode 100644 index e4ff8cae7b8..00000000000 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/npm-dependencies/settings.gradle.kts +++ /dev/null @@ -1,8 +0,0 @@ -pluginManagement { - repositories { - mavenLocal() - gradlePluginPortal() - } -} - -rootProject.name = "npm-dependencies" \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/yarn-setup/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/yarn-setup/build.gradle.kts index 6ce4181d492..eb71b3c9bf5 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/yarn-setup/build.gradle.kts +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/yarn-setup/build.gradle.kts @@ -2,7 +2,7 @@ import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnSetupTask import org.jetbrains.kotlin.gradle.targets.js.yarn.yarn plugins { - kotlin("js") version "" + kotlin("js") } group = "com.example" diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/yarn-setup/settings.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/yarn-setup/settings.gradle.kts deleted file mode 100644 index 1cdf80f4490..00000000000 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/yarn-setup/settings.gradle.kts +++ /dev/null @@ -1,8 +0,0 @@ -pluginManagement { - repositories { - mavenLocal() - gradlePluginPortal() - } -} - -rootProject.name = "yarn-setup" \ No newline at end of file