Skip to content

Commit ef4b437

Browse files
committed
Added AppCDS tests.
1 parent a8e376f commit ef4b437

File tree

7 files changed

+198
-17
lines changed

7 files changed

+198
-17
lines changed

gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/dsl/AppCdsSettings.kt

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ internal fun AppCdsConfiguration.runtimeJvmArgs() = buildList {
3131
/**
3232
* The mode of use of AppCDS.
3333
*/
34-
abstract class AppCdsMode : Serializable {
34+
abstract class AppCdsMode(val name: String) : Serializable {
3535

3636
/**
3737
* Whether to generate a classes.jsa archive for the JRE classes.
@@ -70,21 +70,26 @@ abstract class AppCdsMode : Serializable {
7070
*/
7171
internal open fun checkJdkCompatibility(jdkMajorVersion: Int) = Unit
7272

73+
override fun toString() = name
7374

7475
companion object {
7576

7677
/**
77-
* The name of the AppCds archive file.
78+
* The name of the AppCDS archive file.
7879
*/
7980
private const val ARCHIVE_NAME = "app.jsa"
8081

82+
/**
83+
* The AppCDS archive file.
84+
*/
85+
internal const val ARCHIVE_FILE_ARGUMENT = "\$APPDIR/$ARCHIVE_NAME"
86+
8187
/**
8288
* AppCDS is not used.
8389
*/
84-
val None = object : AppCdsMode() {
90+
val None = object : AppCdsMode("None") {
8591
override val generateJreClassesArchive: Boolean get() = false
8692
override fun runtimeJvmArgs() = emptyList<String>()
87-
override fun toString() = "None"
8893
}
8994

9095
/**
@@ -102,12 +107,12 @@ abstract class AppCdsMode : Serializable {
102107
* of the first execution, which also takes a little longer.
103108
*/
104109
@Suppress("unused")
105-
val Auto = object : AppCdsMode() {
110+
val Auto = object : AppCdsMode("Auto") {
106111
private val MIN_JDK_VERSION = 19
107112
override val generateJreClassesArchive: Boolean get() = true
108113
override fun runtimeJvmArgs() =
109114
listOf(
110-
"-XX:SharedArchiveFile=\$APPDIR/$ARCHIVE_NAME",
115+
"-XX:SharedArchiveFile=$ARCHIVE_FILE_ARGUMENT",
111116
"-XX:+AutoCreateSharedArchive"
112117
)
113118
override fun checkJdkCompatibility(jdkMajorVersion: Int) {
@@ -118,7 +123,6 @@ abstract class AppCdsMode : Serializable {
118123
)
119124
}
120125
}
121-
override fun toString() = "Auto"
122126
}
123127

124128
/**
@@ -136,24 +140,22 @@ abstract class AppCdsMode : Serializable {
136140
* the app's classes.
137141
*/
138142
@Suppress("unused")
139-
val Prebuild = object : AppCdsMode() {
143+
val Prebuild = object : AppCdsMode("Prebuild") {
140144
override val generateJreClassesArchive: Boolean get() = true
141145
override val generateAppClassesArchive: Boolean get() = true
142146
override fun appClassesArchiveCreationJvmArgs() =
143147
listOf(
144-
"-XX:ArchiveClassesAtExit=\$APPDIR/$ARCHIVE_NAME",
145-
"-Dcompose.cds.create-archive=true"
148+
"-XX:ArchiveClassesAtExit=$ARCHIVE_FILE_ARGUMENT",
149+
"-Dcompose.appcds.create-archive=true"
146150
)
147151
override fun appClassesArchiveFile(packagedAppRootDir: File): File {
148152
val appDir = packagedAppJarFilesDir(packagedAppRootDir)
149153
return appDir.resolve(ARCHIVE_NAME)
150154
}
151155
override fun runtimeJvmArgs() =
152156
listOf(
153-
"-XX:SharedArchiveFile=\$APPDIR/$ARCHIVE_NAME",
157+
"-XX:SharedArchiveFile=$ARCHIVE_FILE_ARGUMENT",
154158
)
155-
156-
override fun toString() = "Prebuild"
157159
}
158160
}
159161
}

gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/tasks/AbstractCreateAppCdsArchiveTask.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@ abstract class AbstractCreateAppCdsArchiveTask @Inject constructor(
2626
internal abstract val appCdsMode: Property<AppCdsMode>
2727

2828
@Suppress("unused")
29-
@OutputFile
30-
val appCdsArchiveFile: Provider<File> = provider {
29+
@get:OutputFile
30+
val appCdsArchiveFile: File get() {
3131
val packagedAppRootDir = packagedAppRootDir(appImageRootDir.get())
32-
appCdsMode.get().appClassesArchiveFile(packagedAppRootDir)
32+
return appCdsMode.get().appClassesArchiveFile(packagedAppRootDir)
3333
}
3434

3535
// This is needed to correctly describe the dependencies to Gradle.
@@ -44,7 +44,7 @@ abstract class AbstractCreateAppCdsArchiveTask @Inject constructor(
4444
}
4545
}
4646

47-
val appCdsArchiveFile = appCdsArchiveFile.get().relativeTo(appImageRootDir.get().asFile).path
47+
val appCdsArchiveFile = appCdsArchiveFile.relativeTo(appImageRootDir.get().asFile).path
4848
return appImageRootDir.get().asFileTree.matching { it.exclude(appCdsArchiveFile) }
4949
}
5050

gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/DesktopApplicationTest.kt

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
package org.jetbrains.compose.test.tests.integration
77

88
import org.gradle.internal.impldep.org.testng.Assert
9+
import org.gradle.internal.jvm.inspection.JvmVendor
10+
import org.jetbrains.compose.desktop.application.dsl.AppCdsMode
911
import org.jetbrains.compose.internal.utils.MacUtils
1012
import org.jetbrains.compose.internal.utils.OS
1113
import org.jetbrains.compose.internal.utils.currentArch
@@ -33,6 +35,7 @@ import org.junit.jupiter.api.Test
3335
import java.io.File
3436
import java.util.*
3537
import java.util.jar.JarFile
38+
import kotlin.test.assertTrue
3639

3740
class DesktopApplicationTest : GradlePluginTestBase() {
3841
@Test
@@ -571,6 +574,103 @@ class DesktopApplicationTest : GradlePluginTestBase() {
571574
}
572575
}
573576

577+
@Suppress("SameParameterValue")
578+
private fun appCdsProject(
579+
appCdsMode: AppCdsMode,
580+
javaVersion: Int,
581+
javaVendor: JvmVendor.KnownJvmVendor = JvmVendor.KnownJvmVendor.AMAZON
582+
) : TestProject {
583+
return testProject("application/appCds").apply {
584+
modifyText("build.gradle.kts") {
585+
it
586+
.replace("%APP_CDS_MODE%", "AppCdsMode.$appCdsMode")
587+
.replace("%JAVA_VERSION%", "$javaVersion")
588+
.replace("%JVM_VENDOR%", javaVendor.name)
589+
}
590+
}
591+
}
592+
593+
@Test
594+
fun testAppCdsAutoFailsOnJdk17() = with(appCdsProject(AppCdsMode.Auto, 17)) {
595+
fun testRunTask(runTask: String) {
596+
gradleFailure(runTask).checks {
597+
check.logContains("AppCdsMode 'Auto' is not supported on JDK earlier than 19; current is 17")
598+
}
599+
}
600+
601+
testRunTask(":runDistributable")
602+
}
603+
604+
private val loggedArchivePathRegex =
605+
Regex("\\[cds] Opened archive " +
606+
listOf(
607+
".*",
608+
"build",
609+
"compose",
610+
"binaries",
611+
".*",
612+
"app",
613+
".*",
614+
AppCdsMode.ARCHIVE_FILE_ARGUMENT
615+
.replace("\$APPDIR", "app")
616+
.replace(".", "\\.")
617+
).joinToString(File.separator.replace("\\", "\\\\"))
618+
)
619+
620+
@Test
621+
fun testAppCdsAuto() = with(appCdsProject(AppCdsMode.Auto, 21)) {
622+
fun testRunTask(taskName: String) {
623+
gradle(taskName).checks {
624+
check.taskSuccessful(taskName)
625+
check.logContains("[cds] Dumping shared data to file")
626+
}
627+
gradle(taskName).checks {
628+
check.taskSuccessful(taskName)
629+
check.logContainsMatch(loggedArchivePathRegex)
630+
check.logDoesntContain("[cds] Specified shared archive not found")
631+
check.logDoesntContain("[cds] Dumping shared data to file")
632+
check.logDoesntContain("[cds] Initialize dynamic archive failed")
633+
check.logDoesntContain("[cds] An error has occurred while processing the shared archive file")
634+
check.logDoesntContain("[cds] Failed to initialize dynamic archive")
635+
}
636+
gradle(":clean")
637+
}
638+
639+
testRunTask(":runDistributable")
640+
testRunTask(":runReleaseDistributable")
641+
}
642+
643+
@Test
644+
fun testAppCdsPrebuild() = with(appCdsProject(AppCdsMode.Prebuild, 17)) {
645+
fun testPackageAndRun(release: Boolean) {
646+
val releaseTag = if (release) "Release" else ""
647+
val packageTaskName = ":package${releaseTag}DistributionForCurrentOS"
648+
val createAppCdsTaskName = ":create${releaseTag}AppCdsArchive"
649+
val runDistributableTaskName = ":run${releaseTag}Distributable"
650+
gradle(packageTaskName).checks {
651+
check.taskSuccessful(packageTaskName)
652+
check.taskSuccessful(createAppCdsTaskName)
653+
check.logContains("[cds] Dumping shared data to file")
654+
check.logContains("Running app to create archive: true")
655+
}
656+
657+
gradle(runDistributableTaskName).checks {
658+
check.taskSuccessful(runDistributableTaskName)
659+
check.taskUpToDate(createAppCdsTaskName)
660+
check.logContainsMatch(loggedArchivePathRegex)
661+
check.logContains("Running app to create archive: false")
662+
check.logDoesntContain("[cds] Specified shared archive not found")
663+
check.logDoesntContain("[cds] Dumping shared data to file")
664+
check.logDoesntContain("[cds] Initialize dynamic archive failed")
665+
check.logDoesntContain("[cds] An error has occurred while processing the shared archive file")
666+
check.logDoesntContain("[cds] Failed to initialize dynamic archive")
667+
}
668+
}
669+
670+
testPackageAndRun(release = false)
671+
testPackageAndRun(release = true)
672+
}
673+
574674
private fun TestProject.enableJoinOutputJars() {
575675
val enableJoinOutputJars = """
576676
compose.desktop {

gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/utils/assertUtils.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ internal class BuildResultChecks(private val result: BuildResult) {
4343
}
4444
}
4545

46+
fun logContainsMatch(regex: Regex) {
47+
if (!regex.containsMatchIn(result.output)) {
48+
throw AssertionError("Test output does not match expected regular expression: $regex")
49+
}
50+
}
51+
4652
fun logDoesntContain(substring: String) {
4753
if (result.output.contains(substring)) {
4854
throw AssertionError("Test output contains the unexpected string: '$substring'")
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import org.jetbrains.compose.*
2+
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
3+
import org.jetbrains.compose.desktop.application.dsl.AppCdsMode
4+
import org.gradle.internal.jvm.inspection.JvmVendor
5+
import org.gradle.jvm.toolchain.internal.DefaultJvmVendorSpec
6+
7+
plugins {
8+
id("org.jetbrains.kotlin.jvm")
9+
id("org.jetbrains.kotlin.plugin.compose")
10+
id("org.jetbrains.compose")
11+
}
12+
13+
dependencies {
14+
implementation(kotlin("stdlib"))
15+
implementation(compose.desktop.currentOs)
16+
}
17+
18+
compose.desktop {
19+
application {
20+
javaHome = javaToolchains.launcherFor {
21+
languageVersion.set(JavaLanguageVersion.of(%JAVA_VERSION%))
22+
vendor.set(DefaultJvmVendorSpec.of(JvmVendor.KnownJvmVendor.%JVM_VENDOR%))
23+
}.get().metadata.installationPath.asFile.absolutePath
24+
25+
mainClass = "MainKt"
26+
nativeDistributions {
27+
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
28+
packageVersion = "1.0.0"
29+
}
30+
31+
jvmArgs += "-Xshare:on" // This forces failure if AppCDS doesn't work
32+
appCds {
33+
mode = %APP_CDS_MODE%
34+
logging = true
35+
}
36+
}
37+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
pluginManagement {
2+
plugins {
3+
id 'org.jetbrains.kotlin.jvm' version 'KOTLIN_VERSION_PLACEHOLDER'
4+
id 'org.jetbrains.kotlin.plugin.compose' version 'KOTLIN_VERSION_PLACEHOLDER'
5+
id 'org.jetbrains.compose' version 'COMPOSE_GRADLE_PLUGIN_VERSION_PLACEHOLDER'
6+
id("org.gradle.toolchains.foojay-resolver-convention").version("1.0.0")
7+
}
8+
repositories {
9+
mavenLocal()
10+
gradlePluginPortal()
11+
mavenCentral()
12+
google()
13+
maven {
14+
url 'https://maven.pkg.jetbrains.space/public/p/compose/dev'
15+
}
16+
}
17+
}
18+
plugins {
19+
id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
20+
}
21+
dependencyResolutionManagement {
22+
repositories {
23+
mavenCentral()
24+
google()
25+
maven {
26+
url 'https://maven.pkg.jetbrains.space/public/p/compose/dev'
27+
}
28+
mavenLocal()
29+
}
30+
}
31+
rootProject.name = "simple"
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2+
fun main() {
3+
val creatingAppCdsArchive = System.getProperty("compose.appcds.create-archive") != null
4+
println("Running app to create archive: $creatingAppCdsArchive")
5+
}

0 commit comments

Comments
 (0)