mirror of
https://github.com/jlengrand/compose-multiplatform.git
synced 2026-03-10 08:11:20 +00:00
Fix configuration cache issues (#2607)
Fixes #2587 * Test Gradle 7.6+ with configuration cache by default * Make unpacking wasm runtime configuration cache compatible * Make ProGuard integration configuration cache compatible * Fix unresolved reference * Fix configuration cache exception on macOS
This commit is contained in:
@@ -88,6 +88,7 @@ val jar = tasks.named<Jar>("jar") {
|
||||
val supportedGradleVersions = project.property("compose.tests.gradle.versions")
|
||||
.toString().split(",")
|
||||
.map { it.trim() }
|
||||
.map { GradleVersion.version(it) }
|
||||
|
||||
val gradleTestsPattern = "org.jetbrains.compose.test.tests.integration.*"
|
||||
|
||||
@@ -107,9 +108,13 @@ tasks.test {
|
||||
}
|
||||
|
||||
for (gradleVersion in supportedGradleVersions) {
|
||||
tasks.registerVerificationTask<Test>("testGradle-$gradleVersion") {
|
||||
tasks.registerVerificationTask<Test>("testGradle-${gradleVersion.version}") {
|
||||
classpath = tasks.test.get().classpath
|
||||
systemProperty("compose.tests.gradle.version", gradleVersion)
|
||||
|
||||
if (gradleVersion >= GradleVersion.version("7.6")) {
|
||||
systemProperty("compose.tests.gradle.configuration.cache", "true")
|
||||
}
|
||||
systemProperty("compose.tests.gradle.version", gradleVersion.version)
|
||||
filter {
|
||||
includeTestsMatching(gradleTestsPattern)
|
||||
}
|
||||
|
||||
@@ -248,6 +248,11 @@ private fun JvmApplicationContext.configureProguardTask(
|
||||
val settings = buildType.proguard
|
||||
mainClass.set(app.mainClass)
|
||||
proguardVersion.set(settings.version)
|
||||
proguardFiles.from(proguardVersion.map { proguardVersion ->
|
||||
project.configurations.detachedConfiguration(
|
||||
project.dependencies.create("com.guardsquare:proguard-gradle:${proguardVersion}")
|
||||
)
|
||||
})
|
||||
configurationFiles.from(settings.configurationFiles)
|
||||
// ProGuard uses -dontobfuscate option to turn off obfuscation, which is enabled by default
|
||||
// We want to disable obfuscation by default, because often
|
||||
|
||||
@@ -55,7 +55,8 @@ internal fun JvmApplicationContext.packageBuildVersionFor(
|
||||
private fun JvmApplicationDistributions.packageBuildVersionFor(
|
||||
targetFormat: TargetFormat
|
||||
): String? {
|
||||
check(targetFormat.targetOS == OS.MacOS)
|
||||
if (targetFormat.targetOS != OS.MacOS) return null
|
||||
|
||||
val formatSpecificVersion: String? = when (targetFormat) {
|
||||
TargetFormat.AppImage -> null
|
||||
TargetFormat.Dmg -> macOS.dmgPackageBuildVersion
|
||||
|
||||
@@ -5,11 +5,7 @@
|
||||
|
||||
package org.jetbrains.compose.desktop.application.tasks
|
||||
|
||||
import org.gradle.api.file.ConfigurableFileCollection
|
||||
import org.gradle.api.file.Directory
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.file.RegularFile
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.file.*
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.*
|
||||
@@ -52,6 +48,9 @@ abstract class AbstractProguardTask : AbstractComposeDesktopTask() {
|
||||
@get:Input
|
||||
val proguardVersion: Property<String> = objects.notNullProperty()
|
||||
|
||||
@get:InputFiles
|
||||
val proguardFiles: ConfigurableFileCollection = objects.fileCollection()
|
||||
|
||||
@get:Input
|
||||
val javaHome: Property<String> = objects.notNullProperty(System.getProperty("java.home"))
|
||||
|
||||
@@ -74,9 +73,6 @@ abstract class AbstractProguardTask : AbstractComposeDesktopTask() {
|
||||
@TaskAction
|
||||
fun execute() {
|
||||
val javaHome = File(javaHome.get())
|
||||
val proguardFiles = project.configurations.detachedConfiguration(
|
||||
project.dependencies.create("com.guardsquare:proguard-gradle:${proguardVersion.get()}")
|
||||
).files
|
||||
|
||||
fileOperations.clearDirs(destinationDir, workingDir)
|
||||
val destinationDir = destinationDir.ioFile.absoluteFile
|
||||
|
||||
@@ -5,24 +5,87 @@
|
||||
|
||||
package org.jetbrains.compose.experimental.web.internal
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.Configuration
|
||||
import org.gradle.api.artifacts.ResolvedDependency
|
||||
import org.gradle.api.artifacts.UnresolvedDependency
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.jetbrains.compose.ComposeBuildConfig
|
||||
import org.jetbrains.compose.experimental.dsl.ExperimentalWebApplication
|
||||
import org.jetbrains.compose.internal.utils.registerTask
|
||||
import org.jetbrains.compose.experimental.web.tasks.ExperimentalUnpackSkikoWasmRuntimeTask
|
||||
import org.jetbrains.compose.internal.utils.uppercaseFirstChar
|
||||
import org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrTarget
|
||||
|
||||
internal fun KotlinJsIrTarget.configureExperimentalWebApplication(app: ExperimentalWebApplication) {
|
||||
val mainCompilation = compilations.getByName("main")
|
||||
val unpackedRuntimeDir = project.layout.buildDirectory.dir("compose/skiko-wasm/$targetName")
|
||||
val taskName = "unpackSkikoWasmRuntime${targetName.capitalize()}"
|
||||
val taskName = "unpackSkikoWasmRuntime${targetName.uppercaseFirstChar()}"
|
||||
mainCompilation.defaultSourceSet.resources.srcDir(unpackedRuntimeDir)
|
||||
val unpackRuntime = project.registerTask<ExperimentalUnpackSkikoWasmRuntimeTask>(taskName) {
|
||||
runtimeClasspath = project.configurations.getByName(mainCompilation.runtimeDependencyConfigurationName)
|
||||
outputDir.set(unpackedRuntimeDir)
|
||||
|
||||
val skikoJsWasmRuntimeDependency = skikoVersionProvider(project)
|
||||
.map { skikoVersion ->
|
||||
project.dependencies.create("org.jetbrains.skiko:skiko-js-wasm-runtime:$skikoVersion")
|
||||
}
|
||||
val skikoJsWasmRuntimeConfiguration = project.configurations.create("COMPOSE_SKIKO_JS_WASM_RUNTIME").defaultDependencies {
|
||||
it.addLater(skikoJsWasmRuntimeDependency)
|
||||
}
|
||||
mainCompilation.compileKotlinTaskProvider.configure { compileTask ->
|
||||
compileTask.dependsOn(unpackRuntime)
|
||||
val unpackRuntime = project.registerTask<ExperimentalUnpackSkikoWasmRuntimeTask>(taskName) {
|
||||
skikoRuntimeFiles = skikoJsWasmRuntimeConfiguration
|
||||
outputDir.set(unpackedRuntimeDir)
|
||||
}
|
||||
project.tasks.named(mainCompilation.processResourcesTaskName).configure { processResourcesTask ->
|
||||
processResourcesTask.dependsOn(unpackRuntime)
|
||||
}
|
||||
}
|
||||
|
||||
private const val SKIKO_GROUP = "org.jetbrains.skiko"
|
||||
|
||||
private fun skikoVersionProvider(project: Project): Provider<String> {
|
||||
val composeVersion = ComposeBuildConfig.composeVersion
|
||||
val configurationWithSkiko = project.configurations.detachedConfiguration(
|
||||
project.dependencies.create("org.jetbrains.compose.ui:ui-graphics:$composeVersion")
|
||||
)
|
||||
return project.provider {
|
||||
val skikoDependency = configurationWithSkiko.allDependenciesDescriptors.firstOrNull(::isSkikoDependency)
|
||||
skikoDependency?.version
|
||||
?: error("Cannot determine the version of Skiko for Compose '$composeVersion'")
|
||||
}
|
||||
}
|
||||
|
||||
private fun isSkikoDependency(dep: DependencyDescriptor): Boolean =
|
||||
dep.group == SKIKO_GROUP && dep.version != null
|
||||
|
||||
private val Configuration.allDependenciesDescriptors: Sequence<DependencyDescriptor>
|
||||
get() = with (resolvedConfiguration.lenientConfiguration) {
|
||||
allModuleDependencies.asSequence().map { ResolvedDependencyDescriptor(it) } +
|
||||
unresolvedModuleDependencies.asSequence().map { UnresolvedDependencyDescriptor(it) }
|
||||
}
|
||||
|
||||
private abstract class DependencyDescriptor {
|
||||
abstract val group: String?
|
||||
abstract val name: String?
|
||||
abstract val version: String?
|
||||
}
|
||||
|
||||
private class ResolvedDependencyDescriptor(private val dependency: ResolvedDependency) : DependencyDescriptor() {
|
||||
override val group: String?
|
||||
get() = dependency.moduleGroup
|
||||
|
||||
override val name: String?
|
||||
get() = dependency.moduleName
|
||||
|
||||
override val version: String?
|
||||
get() = dependency.moduleVersion
|
||||
}
|
||||
|
||||
private class UnresolvedDependencyDescriptor(private val dependency: UnresolvedDependency) : DependencyDescriptor() {
|
||||
override val group: String?
|
||||
get() = dependency.selector.group
|
||||
|
||||
override val name: String?
|
||||
get() = dependency.selector.name
|
||||
|
||||
override val version: String?
|
||||
get() = dependency.selector.version
|
||||
}
|
||||
@@ -6,53 +6,45 @@
|
||||
package org.jetbrains.compose.experimental.web.tasks
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.artifacts.Configuration
|
||||
import org.gradle.api.artifacts.component.ModuleComponentIdentifier
|
||||
import org.gradle.api.file.ArchiveOperations
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.file.FileSystemOperations
|
||||
import org.gradle.api.tasks.InputFiles
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.jetbrains.compose.internal.utils.clearDirs
|
||||
import org.jetbrains.compose.internal.utils.debug
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
|
||||
abstract class ExperimentalUnpackSkikoWasmRuntimeTask : DefaultTask() {
|
||||
@get:InputFiles
|
||||
lateinit var runtimeClasspath: Configuration
|
||||
lateinit var skikoRuntimeFiles: FileCollection
|
||||
|
||||
@get:OutputDirectory
|
||||
abstract val outputDir: DirectoryProperty
|
||||
|
||||
@get:Inject
|
||||
internal abstract val archiveOperations: ArchiveOperations
|
||||
|
||||
@get:Inject
|
||||
internal abstract val fileOperations: FileSystemOperations
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
fileOperations.clearDirs(outputDir)
|
||||
val runtimeArtifacts = runtimeClasspath.resolvedConfiguration.resolvedArtifacts
|
||||
for (artifact in runtimeArtifacts) {
|
||||
logger.debug { "Checking artifact: id=${artifact.id}, file=${artifact.file}" }
|
||||
val id = artifact.id.componentIdentifier
|
||||
if (id is ModuleComponentIdentifier && id.group == "org.jetbrains.skiko") {
|
||||
logger.debug { "Found skiko artifact: $artifact" }
|
||||
unpackSkikoRuntime(id.version)
|
||||
|
||||
for (file in skikoRuntimeFiles.files) {
|
||||
if (file.name.endsWith(".jar", ignoreCase = true)) {
|
||||
unpackJar(file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun unpackSkikoRuntime(skikoVersion: String) {
|
||||
val skikoRuntimeConfig = project.configurations.detachedConfiguration(
|
||||
project.dependencies.create("org.jetbrains.skiko:skiko-js-wasm-runtime:$skikoVersion")
|
||||
)
|
||||
|
||||
for (file in skikoRuntimeConfig.resolve()) {
|
||||
if (file.name.endsWith(".jar", ignoreCase = true)) {
|
||||
project.copy { copySpec ->
|
||||
copySpec.from(project.zipTree(file))
|
||||
copySpec.into(outputDir)
|
||||
}
|
||||
}
|
||||
private fun unpackJar(file: File) {
|
||||
fileOperations.copy { copySpec ->
|
||||
copySpec.from(archiveOperations.zipTree(file))
|
||||
copySpec.into(outputDir)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@
|
||||
package org.jetbrains.compose.test.tests.integration
|
||||
|
||||
import org.gradle.internal.impldep.org.testng.Assert
|
||||
import org.gradle.testkit.runner.TaskOutcome
|
||||
import org.jetbrains.compose.internal.utils.MacUtils
|
||||
import org.jetbrains.compose.internal.utils.OS
|
||||
import org.jetbrains.compose.internal.utils.currentArch
|
||||
@@ -17,13 +16,12 @@ import org.jetbrains.compose.test.utils.*
|
||||
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
import java.util.jar.JarFile
|
||||
import kotlin.collections.HashSet
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertFalse
|
||||
import org.junit.jupiter.api.Assumptions
|
||||
import org.junit.jupiter.api.Disabled
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.util.jar.JarFile
|
||||
|
||||
class DesktopApplicationTest : GradlePluginTestBase() {
|
||||
@Test
|
||||
@@ -41,25 +39,25 @@ class DesktopApplicationTest : GradlePluginTestBase() {
|
||||
}
|
||||
""".trimIndent()
|
||||
}
|
||||
gradle("run").build().let { result ->
|
||||
assertEquals(TaskOutcome.SUCCESS, result.task(":run")?.outcome)
|
||||
gradle("run").checks {
|
||||
check.taskSuccessful(":run")
|
||||
}
|
||||
gradle("runDistributable").build().let { result ->
|
||||
assertEquals(TaskOutcome.SUCCESS, result.task(":createDistributable")!!.outcome)
|
||||
assertEquals(TaskOutcome.SUCCESS, result.task(":runDistributable")?.outcome)
|
||||
gradle("runDistributable").checks {
|
||||
check.taskSuccessful(":createDistributable")
|
||||
check.taskSuccessful(":runDistributable")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRunMpp() = with(testProject(TestProjects.mpp)) {
|
||||
val logLine = "Kotlin MPP app is running!"
|
||||
gradle("run").build().checks { check ->
|
||||
check.taskOutcome(":run", TaskOutcome.SUCCESS)
|
||||
gradle("run").checks {
|
||||
check.taskSuccessful(":run")
|
||||
check.logContains(logLine)
|
||||
}
|
||||
gradle("runDistributable").build().checks { check ->
|
||||
check.taskOutcome(":createDistributable", TaskOutcome.SUCCESS)
|
||||
check.taskOutcome(":runDistributable", TaskOutcome.SUCCESS)
|
||||
gradle("runDistributable").checks {
|
||||
check.taskSuccessful(":createDistributable")
|
||||
check.taskSuccessful(":runDistributable")
|
||||
check.logContains(logLine)
|
||||
}
|
||||
}
|
||||
@@ -102,7 +100,7 @@ class DesktopApplicationTest : GradlePluginTestBase() {
|
||||
).checkCustomComposeCompiler()
|
||||
|
||||
private fun TestProject.checkCustomComposeCompiler() {
|
||||
gradle(":runDistributable").build().checks {
|
||||
gradle(":runDistributable").checks {
|
||||
val actualMainImage = file("main-image.actual.png")
|
||||
val expectedMainImage = file("main-image.expected.png")
|
||||
assert(actualMainImage.readBytes().contentEquals(expectedMainImage.readBytes())) {
|
||||
@@ -113,8 +111,8 @@ class DesktopApplicationTest : GradlePluginTestBase() {
|
||||
|
||||
@Test
|
||||
fun kotlinDsl(): Unit = with(testProject(TestProjects.jvmKotlinDsl)) {
|
||||
gradle(":packageDistributionForCurrentOS", "--dry-run").build()
|
||||
gradle(":packageReleaseDistributionForCurrentOS", "--dry-run").build()
|
||||
gradle(":packageDistributionForCurrentOS", "--dry-run")
|
||||
gradle(":packageReleaseDistributionForCurrentOS", "--dry-run")
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -146,8 +144,8 @@ class DesktopApplicationTest : GradlePluginTestBase() {
|
||||
}
|
||||
|
||||
checkImageBeforeBuild()
|
||||
gradle(":runReleaseDistributable").build().checks { check ->
|
||||
check.taskOutcome(":proguardReleaseJars", TaskOutcome.SUCCESS)
|
||||
gradle(":runReleaseDistributable").checks {
|
||||
check.taskSuccessful(":proguardReleaseJars")
|
||||
checkImageAfterBuild()
|
||||
assertEqualTextFiles(file("main-methods.actual.txt"), file("main-methods.expected.txt"))
|
||||
}
|
||||
@@ -155,8 +153,8 @@ class DesktopApplicationTest : GradlePluginTestBase() {
|
||||
file("build.gradle").modify { "$it\n$enableObfuscation" }
|
||||
actualMainImage.delete()
|
||||
checkImageBeforeBuild()
|
||||
gradle(":runReleaseDistributable").build().checks { check ->
|
||||
check.taskOutcome(":proguardReleaseJars", TaskOutcome.SUCCESS)
|
||||
gradle(":runReleaseDistributable").checks {
|
||||
check.taskSuccessful(":proguardReleaseJars")
|
||||
checkImageAfterBuild()
|
||||
assertNotEqualTextFiles(file("main-methods.actual.txt"), file("main-methods.expected.txt"))
|
||||
}
|
||||
@@ -178,13 +176,13 @@ class DesktopApplicationTest : GradlePluginTestBase() {
|
||||
}
|
||||
|
||||
val packagingTask = ":packageDistributionForCurrentOS"
|
||||
gradle(packagingTask).build().checks { check ->
|
||||
check.taskOutcome(packagingTask, TaskOutcome.SUCCESS)
|
||||
gradle(packagingTask).checks {
|
||||
check.taskSuccessful(packagingTask)
|
||||
}
|
||||
|
||||
gradle("clean", packagingTask).build().checks { check ->
|
||||
check.taskOutcome(":checkRuntime", TaskOutcome.FROM_CACHE)
|
||||
check.taskOutcome(packagingTask, TaskOutcome.SUCCESS)
|
||||
gradle("clean", packagingTask).checks {
|
||||
check.taskFromCache(":checkRuntime")
|
||||
check.taskSuccessful(packagingTask)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,7 +198,7 @@ class DesktopApplicationTest : GradlePluginTestBase() {
|
||||
|
||||
|
||||
private fun TestProject.testPackageJvmDistributions() {
|
||||
val result = gradle(":packageDistributionForCurrentOS").build()
|
||||
val result = gradle(":packageDistributionForCurrentOS")
|
||||
|
||||
val mainClass = file("build/classes").walk().single { it.isFile && it.name == "MainKt.class" }
|
||||
val bytecodeVersion = readClassFileVersion(mainClass)
|
||||
@@ -236,8 +234,10 @@ class DesktopApplicationTest : GradlePluginTestBase() {
|
||||
} else {
|
||||
Assert.assertEquals(packageFile.name, "TestPackage-1.0.0.$ext", "Unexpected package name")
|
||||
}
|
||||
assertEquals(TaskOutcome.SUCCESS, result.task(":package${ext.uppercaseFirstChar()}")?.outcome)
|
||||
assertEquals(TaskOutcome.SUCCESS, result.task(":packageDistributionForCurrentOS")?.outcome)
|
||||
result.checks {
|
||||
check.taskSuccessful(":package${ext.uppercaseFirstChar()}")
|
||||
check.taskSuccessful(":packageDistributionForCurrentOS")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -278,8 +278,8 @@ class DesktopApplicationTest : GradlePluginTestBase() {
|
||||
}
|
||||
|
||||
private fun TestProject.testPackageUberJarForCurrentOS() {
|
||||
gradle(":packageUberJarForCurrentOS").build().let { result ->
|
||||
assertEquals(TaskOutcome.SUCCESS, result.task(":packageUberJarForCurrentOS")?.outcome)
|
||||
gradle(":packageUberJarForCurrentOS").checks {
|
||||
check.taskSuccessful(":packageUberJarForCurrentOS")
|
||||
|
||||
val resultJarFile = file("build/compose/jars/TestPackage-${currentTarget.id}-1.0.0.jar")
|
||||
resultJarFile.checkExists()
|
||||
@@ -297,9 +297,9 @@ class DesktopApplicationTest : GradlePluginTestBase() {
|
||||
|
||||
@Test
|
||||
fun testModuleClash() = with(testProject(TestProjects.moduleClashCli)) {
|
||||
gradle(":app:runDistributable").build().checks { check ->
|
||||
check.taskOutcome(":app:createDistributable", TaskOutcome.SUCCESS)
|
||||
check.taskOutcome(":app:runDistributable", TaskOutcome.SUCCESS)
|
||||
gradle(":app:runDistributable").checks {
|
||||
check.taskSuccessful(":app:createDistributable")
|
||||
check.taskSuccessful(":app:runDistributable")
|
||||
check.logContains("Called lib1#util()")
|
||||
check.logContains("Called lib2#util()")
|
||||
}
|
||||
@@ -307,8 +307,8 @@ class DesktopApplicationTest : GradlePluginTestBase() {
|
||||
|
||||
@Test
|
||||
fun testJavaLogger() = with(testProject(TestProjects.javaLogger)) {
|
||||
gradle(":runDistributable").build().checks { check ->
|
||||
check.taskOutcome(":runDistributable", TaskOutcome.SUCCESS)
|
||||
gradle(":runDistributable").checks {
|
||||
check.taskSuccessful(":runDistributable")
|
||||
check.logContains("Compose Gradle plugin test log warning!")
|
||||
}
|
||||
}
|
||||
@@ -324,8 +324,8 @@ class DesktopApplicationTest : GradlePluginTestBase() {
|
||||
Assumptions.assumeTrue(currentOS == OS.MacOS)
|
||||
|
||||
with(testProject(TestProjects.macOptions)) {
|
||||
gradle(":runDistributable").build().checks { check ->
|
||||
check.taskOutcome(":runDistributable", TaskOutcome.SUCCESS)
|
||||
gradle(":runDistributable").checks {
|
||||
check.taskSuccessful(":runDistributable")
|
||||
check.logContains("Hello, from Mac OS!")
|
||||
val appDir = testWorkDir.resolve("build/compose/binaries/main/app/TestPackage.app/Contents/")
|
||||
val actualInfoPlist = appDir.resolve("Info.plist").checkExists()
|
||||
@@ -373,8 +373,8 @@ class DesktopApplicationTest : GradlePluginTestBase() {
|
||||
security("default-keychain", "-s", keychain)
|
||||
security("unlock-keychain", "-p", password, keychain)
|
||||
|
||||
gradle(":createDistributable").build().checks { check ->
|
||||
check.taskOutcome(":createDistributable", TaskOutcome.SUCCESS)
|
||||
gradle(":createDistributable").checks {
|
||||
check.taskSuccessful(":createDistributable")
|
||||
val appDir = testWorkDir.resolve("build/compose/binaries/main/app/TestPackage.app/")
|
||||
val result = runProcess(MacUtils.codesign, args = listOf("--verify", "--verbose", appDir.absolutePath))
|
||||
val actualOutput = result.err.trim()
|
||||
@@ -385,8 +385,8 @@ class DesktopApplicationTest : GradlePluginTestBase() {
|
||||
Assert.assertEquals(expectedOutput, actualOutput)
|
||||
}
|
||||
|
||||
gradle(":runDistributable").build().checks { check ->
|
||||
check.taskOutcome(":runDistributable", TaskOutcome.SUCCESS)
|
||||
gradle(":runDistributable").checks {
|
||||
check.taskSuccessful(":runDistributable")
|
||||
check.logContains("Signed app successfully started!")
|
||||
}
|
||||
}
|
||||
@@ -397,8 +397,8 @@ class DesktopApplicationTest : GradlePluginTestBase() {
|
||||
fun testOptionsWithSpaces() {
|
||||
with(testProject(TestProjects.optionsWithSpaces)) {
|
||||
fun testRunTask(runTask: String) {
|
||||
gradle(runTask).build().checks { check ->
|
||||
check.taskOutcome(runTask, TaskOutcome.SUCCESS)
|
||||
gradle(runTask).checks {
|
||||
check.taskSuccessful(runTask)
|
||||
check.logContains("Running test options with spaces!")
|
||||
check.logContains("Arg #1=Value 1!")
|
||||
check.logContains("Arg #2=Value 2!")
|
||||
@@ -409,8 +409,8 @@ class DesktopApplicationTest : GradlePluginTestBase() {
|
||||
testRunTask(":runDistributable")
|
||||
testRunTask(":run")
|
||||
|
||||
gradle(":packageDistributionForCurrentOS").build().checks { check ->
|
||||
check.taskOutcome(":packageDistributionForCurrentOS", TaskOutcome.SUCCESS)
|
||||
gradle(":packageDistributionForCurrentOS").checks {
|
||||
check.taskSuccessful(":packageDistributionForCurrentOS")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -419,8 +419,8 @@ class DesktopApplicationTest : GradlePluginTestBase() {
|
||||
fun testDefaultArgs() {
|
||||
with(testProject(TestProjects.defaultArgs)) {
|
||||
fun testRunTask(runTask: String) {
|
||||
gradle(runTask).build().checks { check ->
|
||||
check.taskOutcome(runTask, TaskOutcome.SUCCESS)
|
||||
gradle(runTask).checks {
|
||||
check.taskSuccessful(runTask)
|
||||
check.logContains("compose.application.configure.swing.globals=true")
|
||||
}
|
||||
}
|
||||
@@ -428,8 +428,8 @@ class DesktopApplicationTest : GradlePluginTestBase() {
|
||||
testRunTask(":runDistributable")
|
||||
testRunTask(":run")
|
||||
|
||||
gradle(":packageDistributionForCurrentOS").build().checks { check ->
|
||||
check.taskOutcome(":packageDistributionForCurrentOS", TaskOutcome.SUCCESS)
|
||||
gradle(":packageDistributionForCurrentOS").checks {
|
||||
check.taskSuccessful(":packageDistributionForCurrentOS")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -438,8 +438,8 @@ class DesktopApplicationTest : GradlePluginTestBase() {
|
||||
fun testDefaultArgsOverride() {
|
||||
with(testProject(TestProjects.defaultArgsOverride)) {
|
||||
fun testRunTask(runTask: String) {
|
||||
gradle(runTask).build().checks { check ->
|
||||
check.taskOutcome(runTask, TaskOutcome.SUCCESS)
|
||||
gradle(runTask).checks {
|
||||
check.taskSuccessful(runTask)
|
||||
check.logContains("compose.application.configure.swing.globals=false")
|
||||
}
|
||||
}
|
||||
@@ -447,8 +447,8 @@ class DesktopApplicationTest : GradlePluginTestBase() {
|
||||
testRunTask(":runDistributable")
|
||||
testRunTask(":run")
|
||||
|
||||
gradle(":packageDistributionForCurrentOS").build().checks { check ->
|
||||
check.taskOutcome(":packageDistributionForCurrentOS", TaskOutcome.SUCCESS)
|
||||
gradle(":packageDistributionForCurrentOS").checks {
|
||||
check.taskSuccessful(":packageDistributionForCurrentOS")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -456,8 +456,8 @@ class DesktopApplicationTest : GradlePluginTestBase() {
|
||||
@Test
|
||||
fun testSuggestModules() {
|
||||
with(testProject(TestProjects.jvm)) {
|
||||
gradle(":suggestRuntimeModules").build().checks { check ->
|
||||
check.taskOutcome(":suggestRuntimeModules", TaskOutcome.SUCCESS)
|
||||
gradle(":suggestRuntimeModules").checks {
|
||||
check.taskSuccessful(":suggestRuntimeModules")
|
||||
check.logContains("Suggested runtime modules to include:")
|
||||
check.logContains("modules(\"java.instrument\", \"jdk.unsupported\")")
|
||||
}
|
||||
@@ -467,8 +467,8 @@ class DesktopApplicationTest : GradlePluginTestBase() {
|
||||
@Test
|
||||
fun testUnpackSkiko() {
|
||||
with(testProject(TestProjects.unpackSkiko)) {
|
||||
gradle(":runDistributable").build().checks { check ->
|
||||
check.taskOutcome(":runDistributable", TaskOutcome.SUCCESS)
|
||||
gradle(":runDistributable").checks {
|
||||
check.taskSuccessful(":runDistributable")
|
||||
|
||||
val libraryPathPattern = "Read skiko library path: '(.*)'".toRegex()
|
||||
val m = libraryPathPattern.find(check.log)
|
||||
@@ -490,12 +490,12 @@ class DesktopApplicationTest : GradlePluginTestBase() {
|
||||
|
||||
@Test
|
||||
fun resources() = with(testProject(TestProjects.resources)) {
|
||||
gradle(":run").build().checks { check ->
|
||||
check.taskOutcome(":run", TaskOutcome.SUCCESS)
|
||||
gradle(":run").checks {
|
||||
check.taskSuccessful(":run")
|
||||
}
|
||||
|
||||
gradle(":runDistributable").build().checks { check ->
|
||||
check.taskOutcome(":runDistributable", TaskOutcome.SUCCESS)
|
||||
gradle(":runDistributable").checks {
|
||||
check.taskSuccessful(":runDistributable")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,17 @@ import java.net.SocketTimeoutException
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import kotlin.concurrent.thread
|
||||
import org.gradle.testkit.runner.TaskOutcome
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class GradlePluginTest : GradlePluginTestBase() {
|
||||
@Test
|
||||
fun skikoWasm() = with(testProject(TestProjects.skikoWasm)) {
|
||||
gradle(":build").checks {
|
||||
check.taskSuccessful(":unpackSkikoWasmRuntimeJs")
|
||||
check.taskSuccessful(":compileKotlinJs")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun jsMppIsNotBroken() =
|
||||
with(
|
||||
@@ -30,18 +37,13 @@ class GradlePluginTest : GradlePluginTestBase() {
|
||||
)
|
||||
)
|
||||
) {
|
||||
gradle(":compileKotlinJs").build().checks { check ->
|
||||
check.taskOutcome(":compileKotlinJs", TaskOutcome.SUCCESS)
|
||||
gradle(":compileKotlinJs").checks {
|
||||
check.taskSuccessful(":compileKotlinJs")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun configurePreviewWithoutConfigurationCache() = configurePreview(withConfigurationCache = false)
|
||||
|
||||
@Test
|
||||
fun configurePreviewWithConfigurationCache() = configurePreview(withConfigurationCache = true)
|
||||
|
||||
private fun configurePreview(withConfigurationCache: Boolean) {
|
||||
fun configurePreview() {
|
||||
val isAlive = AtomicBoolean(true)
|
||||
val receivedConfigCount = AtomicInteger(0)
|
||||
val port = AtomicInteger(-1)
|
||||
@@ -82,7 +84,7 @@ class GradlePluginTest : GradlePluginTestBase() {
|
||||
}
|
||||
|
||||
try {
|
||||
testConfigureDesktopPreviewImpl(port.get(), withConfigurationCache)
|
||||
testConfigureDesktopPreviewImpl(port.get())
|
||||
} finally {
|
||||
isAlive.set(false)
|
||||
connectionThread.interrupt()
|
||||
@@ -96,35 +98,20 @@ class GradlePluginTest : GradlePluginTestBase() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun testConfigureDesktopPreviewImpl(port: Int, withConfigurationCache: Boolean) {
|
||||
private fun testConfigureDesktopPreviewImpl(port: Int) {
|
||||
check(port > 0) { "Invalid port: $port" }
|
||||
with(testProject(TestProjects.jvmPreview)) {
|
||||
val portProperty = "-Pcompose.desktop.preview.ide.port=$port"
|
||||
val previewTargetProperty = "-Pcompose.desktop.preview.target=PreviewKt.ExamplePreview"
|
||||
val jvmTask = ":jvm:configureDesktopPreview"
|
||||
val configurationCacheArg = "--configuration-cache"
|
||||
val jvmRunner = if (withConfigurationCache) {
|
||||
gradle(jvmTask, portProperty, previewTargetProperty, configurationCacheArg)
|
||||
} else {
|
||||
gradle(jvmTask, portProperty, previewTargetProperty)
|
||||
gradle(jvmTask, portProperty, previewTargetProperty).checks {
|
||||
check.taskSuccessful(jvmTask)
|
||||
}
|
||||
jvmRunner
|
||||
.build()
|
||||
.checks { check ->
|
||||
check.taskOutcome(jvmTask, TaskOutcome.SUCCESS)
|
||||
}
|
||||
|
||||
val mppTask = ":mpp:configureDesktopPreviewDesktop"
|
||||
val mppRunner = if (withConfigurationCache) {
|
||||
gradle(mppTask, portProperty, previewTargetProperty, configurationCacheArg)
|
||||
} else {
|
||||
gradle(mppTask, portProperty, previewTargetProperty)
|
||||
gradle(mppTask, portProperty, previewTargetProperty).checks {
|
||||
check.taskSuccessful(mppTask)
|
||||
}
|
||||
mppRunner
|
||||
.build()
|
||||
.checks { check ->
|
||||
check.taskOutcome(mppTask, TaskOutcome.SUCCESS)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,13 +5,10 @@
|
||||
|
||||
package org.jetbrains.compose.test.tests.integration
|
||||
|
||||
import org.gradle.testkit.runner.TaskOutcome
|
||||
import org.gradle.testkit.runner.UnexpectedBuildFailure
|
||||
import org.jetbrains.compose.test.utils.GradlePluginTestBase
|
||||
import org.jetbrains.compose.test.utils.TestProjects
|
||||
import org.jetbrains.compose.test.utils.checks
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
|
||||
class KotlinCompatabilityTest : GradlePluginTestBase() {
|
||||
@Test
|
||||
@@ -33,8 +30,8 @@ class KotlinCompatabilityTest : GradlePluginTestBase() {
|
||||
)
|
||||
) {
|
||||
val logLine = "Kotlin MPP app is running!"
|
||||
gradle("run").build().checks { check ->
|
||||
check.taskOutcome(":run", TaskOutcome.SUCCESS)
|
||||
gradle("run").checks {
|
||||
check.taskSuccessful(":run")
|
||||
check.logContains(logLine)
|
||||
}
|
||||
}
|
||||
@@ -45,8 +42,8 @@ class KotlinCompatabilityTest : GradlePluginTestBase() {
|
||||
testEnvironment = defaultTestEnvironment.copy(kotlinVersion = kotlinVersion)
|
||||
)
|
||||
) {
|
||||
gradle(":compileKotlinJs").build().checks { check ->
|
||||
check.taskOutcome(":compileKotlinJs", TaskOutcome.SUCCESS)
|
||||
gradle(":compileKotlinJs").checks {
|
||||
check.taskSuccessful(":compileKotlinJs")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
|
||||
package org.jetbrains.compose.test.utils
|
||||
|
||||
import org.gradle.testkit.runner.BuildResult
|
||||
import org.gradle.testkit.runner.GradleRunner
|
||||
import org.gradle.util.GradleVersion
|
||||
import org.jetbrains.compose.desktop.application.internal.ComposeProperties
|
||||
import java.io.File
|
||||
import java.util.Properties
|
||||
@@ -64,18 +66,48 @@ class TestProject(
|
||||
}
|
||||
}
|
||||
|
||||
fun gradle(vararg args: String): GradleRunner =
|
||||
GradleRunner.create().apply {
|
||||
withGradleVersion(TestProperties.gradleVersionForTests)
|
||||
withProjectDir(testEnvironment.workingDir)
|
||||
withArguments(args.toList() + additionalArgs)
|
||||
forwardOutput()
|
||||
internal fun gradle(vararg args: String): BuildResult {
|
||||
if (TestProperties.gradleConfigurationCache) {
|
||||
if (GradleVersion.version(TestProperties.gradleVersionForTests) < GradleVersion.version("8.0-rc-1")) {
|
||||
// Gradle 7.* does not use the configuration cache in the same build.
|
||||
// In other words, if cache misses, Gradle performs configuration,
|
||||
// but does not, use the serialized task graph.
|
||||
// So in order to test the cache, we need to perform dry-run before the actual run.
|
||||
// This should be fixed in https://github.com/gradle/gradle/issues/21985 (which is planned for 8.0 RC 1)
|
||||
gradleRunner(args.withDryRun()).build()
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DeprecatedCallableAddReplaceWith")
|
||||
@Deprecated("Do not commit!")
|
||||
fun gradleDebug(vararg args: String): GradleRunner =
|
||||
gradle(*args).withDebug(true)
|
||||
return gradleRunner(args).build()
|
||||
}
|
||||
|
||||
private fun Array<out String>.withDryRun(): Array<String> {
|
||||
var sawDryRun = false
|
||||
val dryRunArgs = ArrayList<String>(size)
|
||||
for (arg in this) {
|
||||
sawDryRun = sawDryRun || arg.trim() in listOf("-m", "--dry-run")
|
||||
dryRunArgs.add(arg)
|
||||
}
|
||||
if (!sawDryRun) {
|
||||
dryRunArgs.add("--dry-run")
|
||||
}
|
||||
return dryRunArgs.toTypedArray()
|
||||
}
|
||||
|
||||
private fun gradleRunner(args: Array<out String>): GradleRunner {
|
||||
val allArgs = args.toMutableList()
|
||||
allArgs.addAll(additionalArgs)
|
||||
if (TestProperties.gradleConfigurationCache) {
|
||||
allArgs.add("--configuration-cache")
|
||||
}
|
||||
|
||||
return GradleRunner.create().apply {
|
||||
withGradleVersion(TestProperties.gradleVersionForTests)
|
||||
withProjectDir(testEnvironment.workingDir)
|
||||
withArguments(allArgs)
|
||||
forwardOutput()
|
||||
}
|
||||
}
|
||||
|
||||
fun file(path: String): File =
|
||||
testEnvironment.workingDir.resolve(path)
|
||||
|
||||
@@ -22,5 +22,6 @@ object TestProjects {
|
||||
const val unpackSkiko = "application/unpackSkiko"
|
||||
const val resources = "application/resources"
|
||||
const val jsMpp = "misc/jsMpp"
|
||||
const val skikoWasm = "misc/skikoWasm"
|
||||
const val jvmPreview = "misc/jvmPreview"
|
||||
}
|
||||
@@ -23,6 +23,9 @@ object TestProperties {
|
||||
val gradleVersionForTests: String?
|
||||
get() = System.getProperty("compose.tests.gradle.version")
|
||||
|
||||
val gradleConfigurationCache: Boolean
|
||||
get() = System.getProperty("compose.tests.gradle.configuration.cache") == "true"
|
||||
|
||||
val summaryFile: File?
|
||||
get() = System.getProperty("compose.tests.summary.file")?.let { File(it) }
|
||||
|
||||
|
||||
@@ -18,10 +18,13 @@ internal fun <T> Collection<T>.checkContains(vararg elements: T) {
|
||||
}
|
||||
}
|
||||
|
||||
internal fun BuildResult.checks(fn: (BuildResultChecks) -> Unit) {
|
||||
fn(BuildResultChecks(this))
|
||||
internal fun BuildResult.checks(fn: ChecksWrapper.() -> Unit) {
|
||||
fn(ChecksWrapper(BuildResultChecks(this)))
|
||||
}
|
||||
|
||||
@JvmInline
|
||||
internal value class ChecksWrapper(val check: BuildResultChecks)
|
||||
|
||||
internal class BuildResultChecks(private val result: BuildResult) {
|
||||
val log: String
|
||||
get() = result.output
|
||||
@@ -32,7 +35,19 @@ internal class BuildResultChecks(private val result: BuildResult) {
|
||||
}
|
||||
}
|
||||
|
||||
fun taskOutcome(task: String, expectedOutcome: TaskOutcome) {
|
||||
fun taskSuccessful(task: String) {
|
||||
taskOutcome(task, TaskOutcome.SUCCESS)
|
||||
}
|
||||
|
||||
fun taskFailed(task: String) {
|
||||
taskOutcome(task, TaskOutcome.FAILED)
|
||||
}
|
||||
|
||||
fun taskFromCache(task: String) {
|
||||
taskOutcome(task, TaskOutcome.FROM_CACHE)
|
||||
}
|
||||
|
||||
private fun taskOutcome(task: String, expectedOutcome: TaskOutcome) {
|
||||
val actualOutcome = result.task(task)?.outcome
|
||||
if (actualOutcome != expectedOutcome) {
|
||||
throw AssertionError(
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
|
||||
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.multiplatform"
|
||||
id "org.jetbrains.compose"
|
||||
}
|
||||
|
||||
repositories {
|
||||
jetbrainsCompose()
|
||||
}
|
||||
|
||||
kotlin {
|
||||
js(IR) {
|
||||
browser()
|
||||
binaries.executable()
|
||||
}
|
||||
sourceSets {
|
||||
commonMain {
|
||||
dependencies {
|
||||
implementation(compose.ui)
|
||||
implementation(compose.foundation)
|
||||
implementation(compose.material)
|
||||
implementation(compose.runtime)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
compose.experimental {
|
||||
web.application {}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
org.jetbrains.compose.experimental.jscanvas.enabled=true
|
||||
@@ -0,0 +1,12 @@
|
||||
pluginManagement {
|
||||
plugins {
|
||||
id 'org.jetbrains.kotlin.multiplatform' version 'KOTLIN_VERSION_PLACEHOLDER'
|
||||
id 'org.jetbrains.compose' version 'COMPOSE_GRADLE_PLUGIN_VERSION_PLACEHOLDER'
|
||||
}
|
||||
repositories {
|
||||
mavenLocal()
|
||||
gradlePluginPortal()
|
||||
maven { url "https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev" }
|
||||
}
|
||||
}
|
||||
rootProject.name = "skikoWasm"
|
||||
@@ -0,0 +1,7 @@
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.*
|
||||
|
||||
@Composable
|
||||
fun HelloWorld() {
|
||||
Text("Hello, World!")
|
||||
}
|
||||
Reference in New Issue
Block a user