From 63334b346cbf4eeb5f9278c798df36e6a0ad5390 Mon Sep 17 00:00:00 2001 From: Konstantin Tskhovrebov Date: Fri, 9 Feb 2024 19:08:23 +0100 Subject: [PATCH 1/7] Add hierarchical resource to the demo --- .../composeResources/files/platform-text.txt | 1 + .../compose/resources/demo/shared/FileRes.kt | 49 +++++++++++++------ .../composeResources/files/platform-text.txt | 1 + .../composeResources/files/platform-text.txt | 1 + .../composeResources/files/platform-text.txt | 1 + .../composeResources/files/platform-text.txt | 1 + .../composeResources/files/platform-text.txt | 1 + 7 files changed, 41 insertions(+), 14 deletions(-) create mode 100644 components/resources/demo/shared/src/androidMain/composeResources/files/platform-text.txt create mode 100644 components/resources/demo/shared/src/desktopMain/composeResources/files/platform-text.txt create mode 100644 components/resources/demo/shared/src/iosMain/composeResources/files/platform-text.txt create mode 100644 components/resources/demo/shared/src/jsMain/composeResources/files/platform-text.txt create mode 100644 components/resources/demo/shared/src/macosMain/composeResources/files/platform-text.txt create mode 100644 components/resources/demo/shared/src/wasmJsMain/composeResources/files/platform-text.txt diff --git a/components/resources/demo/shared/src/androidMain/composeResources/files/platform-text.txt b/components/resources/demo/shared/src/androidMain/composeResources/files/platform-text.txt new file mode 100644 index 00000000000..30cd8eb8bcd --- /dev/null +++ b/components/resources/demo/shared/src/androidMain/composeResources/files/platform-text.txt @@ -0,0 +1 @@ +Android platform \ No newline at end of file diff --git a/components/resources/demo/shared/src/commonMain/kotlin/org/jetbrains/compose/resources/demo/shared/FileRes.kt b/components/resources/demo/shared/src/commonMain/kotlin/org/jetbrains/compose/resources/demo/shared/FileRes.kt index 57f1c65b598..efe4b687374 100644 --- a/components/resources/demo/shared/src/commonMain/kotlin/org/jetbrains/compose/resources/demo/shared/FileRes.kt +++ b/components/resources/demo/shared/src/commonMain/kotlin/org/jetbrains/compose/resources/demo/shared/FileRes.kt @@ -1,22 +1,11 @@ package org.jetbrains.compose.resources.demo.shared -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.CardDefaults -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.OutlinedCard -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue +import androidx.compose.material3.* +import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import components.resources.demo.shared.generated.resources.Res @@ -59,5 +48,37 @@ fun FileRes(paddingValues: PaddingValues) { Text(bytes.decodeToString()) """.trimIndent() ) + Text( + modifier = Modifier.padding(16.dp), + text = "File: 'files/platform-text.txt'", + style = MaterialTheme.typography.titleLarge + ) + OutlinedCard( + modifier = Modifier.padding(horizontal = 16.dp), + shape = RoundedCornerShape(4.dp), + colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.primaryContainer) + ) { + var bytes by remember { mutableStateOf(ByteArray(0)) } + LaunchedEffect(Unit) { + bytes = Res.readBytes("files/platform-text.txt") + } + Text( + modifier = Modifier.padding(8.dp), + text = bytes.decodeToString(), + color = MaterialTheme.colorScheme.onPrimaryContainer + ) + } + Text( + modifier = Modifier.padding(16.dp), + text = """ + var bytes by remember { + mutableStateOf(ByteArray(0)) + } + LaunchedEffect(Unit) { + bytes = Res.readFileBytes("files/platform-text.txt") + } + Text(bytes.decodeToString()) + """.trimIndent() + ) } } \ No newline at end of file diff --git a/components/resources/demo/shared/src/desktopMain/composeResources/files/platform-text.txt b/components/resources/demo/shared/src/desktopMain/composeResources/files/platform-text.txt new file mode 100644 index 00000000000..e4be1a9d0b0 --- /dev/null +++ b/components/resources/demo/shared/src/desktopMain/composeResources/files/platform-text.txt @@ -0,0 +1 @@ +Desktop platform \ No newline at end of file diff --git a/components/resources/demo/shared/src/iosMain/composeResources/files/platform-text.txt b/components/resources/demo/shared/src/iosMain/composeResources/files/platform-text.txt new file mode 100644 index 00000000000..05dccd14997 --- /dev/null +++ b/components/resources/demo/shared/src/iosMain/composeResources/files/platform-text.txt @@ -0,0 +1 @@ +iOS platform \ No newline at end of file diff --git a/components/resources/demo/shared/src/jsMain/composeResources/files/platform-text.txt b/components/resources/demo/shared/src/jsMain/composeResources/files/platform-text.txt new file mode 100644 index 00000000000..531c43b14ab --- /dev/null +++ b/components/resources/demo/shared/src/jsMain/composeResources/files/platform-text.txt @@ -0,0 +1 @@ +JS platform \ No newline at end of file diff --git a/components/resources/demo/shared/src/macosMain/composeResources/files/platform-text.txt b/components/resources/demo/shared/src/macosMain/composeResources/files/platform-text.txt new file mode 100644 index 00000000000..c0ab602c161 --- /dev/null +++ b/components/resources/demo/shared/src/macosMain/composeResources/files/platform-text.txt @@ -0,0 +1 @@ +macOS platform \ No newline at end of file diff --git a/components/resources/demo/shared/src/wasmJsMain/composeResources/files/platform-text.txt b/components/resources/demo/shared/src/wasmJsMain/composeResources/files/platform-text.txt new file mode 100644 index 00000000000..100bd90dfaa --- /dev/null +++ b/components/resources/demo/shared/src/wasmJsMain/composeResources/files/platform-text.txt @@ -0,0 +1 @@ +WasmJS platform \ No newline at end of file From a912c9fe99fd290ab74fbd55e9770395f59fd6f5 Mon Sep 17 00:00:00 2001 From: Konstantin Tskhovrebov Date: Fri, 9 Feb 2024 19:06:30 +0100 Subject: [PATCH 2/7] Register all hierarchical compose resources in android compilation --- .../resources/AndroidTargetConfiguration.kt | 52 -------- .../compose/resources/ResourcesGenerator.kt | 112 +++++++++++++++--- 2 files changed, 95 insertions(+), 69 deletions(-) delete mode 100644 gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/AndroidTargetConfiguration.kt diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/AndroidTargetConfiguration.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/AndroidTargetConfiguration.kt deleted file mode 100644 index d251a82b547..00000000000 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/AndroidTargetConfiguration.kt +++ /dev/null @@ -1,52 +0,0 @@ -package org.jetbrains.compose.resources - -import com.android.build.api.variant.AndroidComponentsExtension -import com.android.build.gradle.BaseExtension -import org.gradle.api.Project -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.provider.Provider -import org.gradle.api.tasks.* -import org.jetbrains.compose.internal.utils.registerTask -import org.jetbrains.compose.internal.utils.uppercaseFirstChar -import java.io.File - -internal fun Project.configureAndroidResources( - commonResourcesDir: Provider, - androidFontsDir: Provider, - onlyIfProvider: Provider -) { - val androidExtension = project.extensions.findByName("android") as? BaseExtension ?: return - val androidComponents = project.extensions.findByType(AndroidComponentsExtension::class.java) ?: return - - val androidMainSourceSet = androidExtension.sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME) - androidMainSourceSet.resources.srcDir(commonResourcesDir) - androidMainSourceSet.assets.srcDir(androidFontsDir) - - androidComponents.onVariants { variant -> - val copyFonts = registerTask( - "copy${variant.name.uppercaseFirstChar()}FontsToAndroidAssets" - ) { - includeEmptyDirs = false - from(commonResourcesDir) - include("**/font*/*") - onlyIf { onlyIfProvider.get() } - } - variant.sources?.assets?.addGeneratedSourceDirectory( - taskProvider = copyFonts, - wiredWith = CopyAndroidAssetsTask::outputDirectory - ) - } -} - -//https://github.com/JetBrains/compose-multiplatform/issues/4085 -private abstract class CopyAndroidAssetsTask : Copy() { - @get:OutputDirectory - abstract val outputDirectory: DirectoryProperty - - override fun getDestinationDir(): File = - outputDirectory.get().asFile - - override fun setDestinationDir(destination: File) { - outputDirectory.set(destination) - } -} \ No newline at end of file diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ResourcesGenerator.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ResourcesGenerator.kt index 9378ac67f98..4fa93afdd39 100644 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ResourcesGenerator.kt +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ResourcesGenerator.kt @@ -1,18 +1,35 @@ package org.jetbrains.compose.resources +import com.android.build.api.variant.AndroidComponentsExtension +import com.android.build.gradle.BaseExtension +import com.android.build.gradle.internal.tasks.ProcessJavaResTask +import org.gradle.api.DefaultTask import org.gradle.api.Project +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.FileSystemOperations +import org.gradle.api.provider.Property import org.gradle.api.provider.Provider -import org.gradle.api.tasks.SourceSet +import org.gradle.api.tasks.* import org.jetbrains.compose.ComposePlugin import org.jetbrains.compose.desktop.application.internal.ComposeProperties import org.jetbrains.compose.internal.KOTLIN_JVM_PLUGIN_ID import org.jetbrains.compose.internal.KOTLIN_MPP_PLUGIN_ID +import org.jetbrains.compose.internal.utils.* +import org.jetbrains.compose.internal.utils.dependsOn +import org.jetbrains.compose.internal.utils.registerTask +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension +import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget +import org.jetbrains.kotlin.gradle.plugin.sources.android.androidSourceSetInfoOrNull +import org.jetbrains.kotlin.gradle.utils.ObservableSet import java.io.File +import javax.inject.Inject internal const val COMPOSE_RESOURCES_DIR = "composeResources" -private const val RES_GEN_DIR = "generated/compose/resourceGenerator" +internal const val RES_GEN_DIR = "generated/compose/resourceGenerator" private val androidPluginIds = listOf( "com.android.application", "com.android.library" @@ -20,27 +37,77 @@ private val androidPluginIds = listOf( internal fun Project.configureComposeResources() { plugins.withId(KOTLIN_MPP_PLUGIN_ID) { - configureComposeResources(KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME) + val kotlinExtension = project.extensions.getByType(KotlinMultiplatformExtension::class.java) + configureComposeResources(kotlinExtension, KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME) + + //when applied AGP then configure android resources + androidPluginIds.forEach { pluginId -> + plugins.withId(pluginId) { + val androidExtension = project.extensions.getByType(BaseExtension::class.java) + configureAndroidComposeResources(kotlinExtension, androidExtension) + } + } } plugins.withId(KOTLIN_JVM_PLUGIN_ID) { - configureComposeResources(SourceSet.MAIN_SOURCE_SET_NAME) + val kotlinExtension = project.extensions.getByType(KotlinProjectExtension::class.java) + configureComposeResources(kotlinExtension, SourceSet.MAIN_SOURCE_SET_NAME) } } -private fun Project.configureComposeResources(commonSourceSetName: String) { - val kotlinExtension = project.extensions.getByType(KotlinProjectExtension::class.java) +private fun Project.configureComposeResources(kotlinExtension: KotlinProjectExtension, commonSourceSetName: String) { kotlinExtension.sourceSets.all { sourceSet -> val sourceSetName = sourceSet.name val composeResourcesPath = project.projectDir.resolve("src/$sourceSetName/$COMPOSE_RESOURCES_DIR") + + //To compose resources will be packed to a final artefact we need to mark them as resources + //sourceSet.resources works for all targets except ANDROID! sourceSet.resources.srcDirs(composeResourcesPath) + if (sourceSetName == commonSourceSetName) { configureResourceGenerator(composeResourcesPath, sourceSet) } } } +@OptIn(ExperimentalKotlinGradlePluginApi::class) +private fun Project.configureAndroidComposeResources( + kotlinExtension: KotlinMultiplatformExtension, + androidExtension: BaseExtension +) { + //mark all composeResources as Android resources + kotlinExtension.targets.matching { it is KotlinAndroidTarget }.all { androidTarget -> + androidTarget.compilations.all { compilation: KotlinCompilation<*> -> + compilation.defaultSourceSet.androidSourceSetInfoOrNull?.let { kotlinAndroidSourceSet -> + androidExtension.sourceSets + .matching { it.name == kotlinAndroidSourceSet.androidSourceSetName } + .all { androidSourceSet -> + (compilation.allKotlinSourceSets as ObservableSet).forAll { kotlinSourceSet -> + androidSourceSet.resources.srcDir( + projectDir.resolve("src/${kotlinSourceSet.name}/$COMPOSE_RESOURCES_DIR") + ) + } + } + } + } + } + + //copy fonts from the compose resources dir to android assets + val androidComponents = project.extensions.findByType(AndroidComponentsExtension::class.java) ?: return + val commonResourcesDir = projectDir.resolve("src/${KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME}/$COMPOSE_RESOURCES_DIR") + androidComponents.onVariants { variant -> + val copyFonts = registerTask( + "copy${variant.name.uppercaseFirstChar()}FontsToAndroidAssets" + ) { + from.set(commonResourcesDir) + } + variant.sources?.assets?.addGeneratedSourceDirectory( + taskProvider = copyFonts, + wiredWith = CopyAndroidFontsToAssetsTask::outputDirectory + ) + } +} + private fun Project.configureResourceGenerator(commonComposeResourcesDir: File, commonSourceSet: KotlinSourceSet) { - val commonComposeResources = provider { commonComposeResourcesDir } val packageName = provider { buildString { val group = project.group.toString().lowercase().asUnderscoredIdentifier() @@ -73,7 +140,7 @@ private fun Project.configureResourceGenerator(commonComposeResourcesDir: File, ) { it.packageName.set(packageName) it.shouldGenerateResClass.set(shouldGenerateResClass) - it.resDir.set(commonComposeResources) + it.resDir.set(commonComposeResourcesDir) it.codeDir.set(buildDir("$RES_GEN_DIR/kotlin")) } @@ -86,15 +153,26 @@ private fun Project.configureResourceGenerator(commonComposeResourcesDir: File, it.dependsOn(genTask) } } +} - //when applied AGP then configure android resources - androidPluginIds.forEach { pluginId -> - plugins.withId(pluginId) { - configureAndroidResources( - commonComposeResources, - buildDir("$RES_GEN_DIR/androidFonts").map { it.asFile }, - shouldGenerateResClass - ) +//Copy task doesn't work with 'variant.sources?.assets?.addGeneratedSourceDirectory' API +internal abstract class CopyAndroidFontsToAssetsTask : DefaultTask() { + @get:Inject + abstract val fileSystem: FileSystemOperations + + @get:Input + abstract val from: Property + + @get:OutputDirectory + abstract val outputDirectory: DirectoryProperty + + @TaskAction + fun action() { + fileSystem.copy { + it.includeEmptyDirs = false + it.from(from) + it.exclude("**/font*/*") + it.into(outputDirectory) } } -} +} \ No newline at end of file From 7c0e29ca625e59298da54050e18b0a218c05015d Mon Sep 17 00:00:00 2001 From: Konstantin Tskhovrebov Date: Fri, 9 Feb 2024 19:54:33 +0100 Subject: [PATCH 3/7] Fix android font bundling and add tests --- .../compose/resources/ResourcesGenerator.kt | 2 +- .../test/tests/integration/ResourcesTest.kt | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ResourcesGenerator.kt b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ResourcesGenerator.kt index 4fa93afdd39..801c8af054a 100644 --- a/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ResourcesGenerator.kt +++ b/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/resources/ResourcesGenerator.kt @@ -171,7 +171,7 @@ internal abstract class CopyAndroidFontsToAssetsTask : DefaultTask() { fileSystem.copy { it.includeEmptyDirs = false it.from(from) - it.exclude("**/font*/*") + it.include("**/font*/*") it.into(outputDirectory) } } diff --git a/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt b/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt index 619cf7fba09..a215a18e8d6 100644 --- a/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt +++ b/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt @@ -5,6 +5,8 @@ import org.jetbrains.compose.test.utils.assertEqualTextFiles import org.jetbrains.compose.test.utils.assertNotEqualTextFiles import org.jetbrains.compose.test.utils.checks import org.junit.jupiter.api.Test +import java.util.zip.ZipEntry +import java.util.zip.ZipFile import kotlin.io.path.Path class ResourcesTest : GradlePluginTestBase() { @@ -115,6 +117,18 @@ class ResourcesTest : GradlePluginTestBase() { gradle("build").checks { check.taskSuccessful(":copyDebugFontsToAndroidAssets") check.taskSuccessful(":copyReleaseFontsToAndroidAssets") + + val debugApk = file("build/outputs/apk/debug/resources_test-debug.apk") + assert(debugApk.exists()) + val debugZip = ZipFile(debugApk) + assert(debugZip.getEntry("font/emptyFont.otf") != null) + assert(debugZip.getEntry("assets/font/emptyFont.otf") != null) + + val releaseApk = file("build/outputs/apk/release/resources_test-release-unsigned.apk") + assert(releaseApk.exists()) + val releaseZip = ZipFile(releaseApk) + assert(releaseZip.getEntry("font/emptyFont.otf") != null) + assert(releaseZip.getEntry("assets/font/emptyFont.otf") != null) } } From 61edcf17b17a4f45cdfadd3073e2666d266fb121 Mon Sep 17 00:00:00 2001 From: Konstantin Tskhovrebov Date: Fri, 9 Feb 2024 21:36:40 +0100 Subject: [PATCH 4/7] Check resources in final artefacts --- .../test/tests/integration/ResourcesTest.kt | 105 +++++++++++++++--- .../misc/commonResources/gradle.properties | 3 +- 2 files changed, 91 insertions(+), 17 deletions(-) diff --git a/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt b/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt index a215a18e8d6..a9766e4ad46 100644 --- a/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt +++ b/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt @@ -1,10 +1,11 @@ package org.jetbrains.compose.test.tests.integration -import org.jetbrains.compose.test.utils.GradlePluginTestBase +import org.jetbrains.compose.test.utils.* import org.jetbrains.compose.test.utils.assertEqualTextFiles import org.jetbrains.compose.test.utils.assertNotEqualTextFiles import org.jetbrains.compose.test.utils.checks import org.junit.jupiter.api.Test +import java.io.File import java.util.zip.ZipEntry import java.util.zip.ZipFile import kotlin.io.path.Path @@ -113,22 +114,94 @@ class ResourcesTest : GradlePluginTestBase() { } @Test - fun testCopyFontsInAndroidApp(): Unit = with(testProject("misc/commonResources")) { + fun testFinalArtefacts(): Unit = with(testProject("misc/commonResources")) { + //https://developer.android.com/build/build-variants?utm_source=android-studio#product-flavors + file("build.gradle.kts").appendText(""" + + kotlin { + js { + browser { + testTask(Action { + enabled = false + }) + } + binaries.executable() + } + } + + android { + flavorDimensions += "version" + productFlavors { + create("demo") + create("full") + } + } + """.trimIndent()) + file("src/androidDemoDebug/composeResources/files/platform.txt").writeNewFile("android demo-debug") + file("src/androidDemoRelease/composeResources/files/platform.txt").writeNewFile("android demo-release") + file("src/androidFullDebug/composeResources/files/platform.txt").writeNewFile("android full-debug") + file("src/androidFullRelease/composeResources/files/platform.txt").writeNewFile("android full-release") + file("src/desktopMain/composeResources/files/platform.txt").writeNewFile("desktop") + file("src/jsMain/composeResources/files/platform.txt").writeNewFile("js") + + val commonResourcesDir = file("src/commonMain/composeResources") + val commonResourcesFiles = commonResourcesDir.walkTopDown() + .filter { !it.isDirectory && !it.isHidden } + .map { it.relativeTo(commonResourcesDir).path } + gradle("build").checks { - check.taskSuccessful(":copyDebugFontsToAndroidAssets") - check.taskSuccessful(":copyReleaseFontsToAndroidAssets") - - val debugApk = file("build/outputs/apk/debug/resources_test-debug.apk") - assert(debugApk.exists()) - val debugZip = ZipFile(debugApk) - assert(debugZip.getEntry("font/emptyFont.otf") != null) - assert(debugZip.getEntry("assets/font/emptyFont.otf") != null) - - val releaseApk = file("build/outputs/apk/release/resources_test-release-unsigned.apk") - assert(releaseApk.exists()) - val releaseZip = ZipFile(releaseApk) - assert(releaseZip.getEntry("font/emptyFont.otf") != null) - assert(releaseZip.getEntry("assets/font/emptyFont.otf") != null) + check.taskSuccessful(":copyDemoDebugFontsToAndroidAssets") + check.taskSuccessful(":copyDemoReleaseFontsToAndroidAssets") + check.taskSuccessful(":copyFullDebugFontsToAndroidAssets") + check.taskSuccessful(":copyFullReleaseFontsToAndroidAssets") + + checkAndroidApk("demo", "debug", commonResourcesFiles) + checkAndroidApk("demo", "release", commonResourcesFiles) + checkAndroidApk("full", "debug", commonResourcesFiles) + checkAndroidApk("full", "release", commonResourcesFiles) + + val desktopJar = file("build/libs/resources_test-desktop.jar") + assert(desktopJar.exists()) + ZipFile(desktopJar).let { zip -> + commonResourcesFiles.forEach { res -> + assert(zip.getEntry(res) != null) + } + assert(zip.getEntry("files/platform.txt") != null) + val text = zip.getInputStream( + zip.getEntry("files/platform.txt") + ).readBytes().decodeToString() + assert(text == "desktop") + } + + val jsBuildDir = file("build/dist/js/productionExecutable") + commonResourcesFiles.forEach { res -> + assert(jsBuildDir.resolve(res).exists()) + } + assert(jsBuildDir.resolve("files/platform.txt").readText() == "js") + } + } + + private fun File.writeNewFile(text: String) { + parentFile.mkdirs() + createNewFile() + writeText(text) + } + + private fun TestProject.checkAndroidApk(flavor: String, type: String, commonResourcesFiles: Sequence) { + val typeFilePostfix = if (type == "release") "$type-unsigned" else type + val apk = file("build/outputs/apk/$flavor/$type/resources_test-$flavor-$typeFilePostfix.apk") + assert(apk.exists()) + ZipFile(apk).let { zip -> + commonResourcesFiles.forEach { res -> + assert(zip.getEntry(res) != null) + //todo fix duplicate fonts + } + assert(zip.getEntry("assets/font/emptyFont.otf") != null) + assert(zip.getEntry("files/platform.txt") != null) + val text = zip.getInputStream( + zip.getEntry("files/platform.txt") + ).readBytes().decodeToString() + assert(text == "android $flavor-$type") } } diff --git a/gradle-plugins/compose/src/test/test-projects/misc/commonResources/gradle.properties b/gradle-plugins/compose/src/test/test-projects/misc/commonResources/gradle.properties index 6f0ca8b2165..f4d71096639 100644 --- a/gradle-plugins/compose/src/test/test-projects/misc/commonResources/gradle.properties +++ b/gradle-plugins/compose/src/test/test-projects/misc/commonResources/gradle.properties @@ -1,2 +1,3 @@ org.gradle.jvmargs=-Xmx8096M -android.useAndroidX=true \ No newline at end of file +android.useAndroidX=true +org.jetbrains.compose.experimental.jscanvas.enabled=true \ No newline at end of file From 6382f61b28d37104d1e464af2dc6016945caae39 Mon Sep 17 00:00:00 2001 From: Konstantin Tskhovrebov Date: Mon, 12 Feb 2024 12:05:38 +0100 Subject: [PATCH 5/7] Disable apk signing for resources tests --- .../jetbrains/compose/test/tests/integration/ResourcesTest.kt | 3 +-- .../test/test-projects/misc/commonResources/build.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt b/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt index a9766e4ad46..911b8b35564 100644 --- a/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt +++ b/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt @@ -188,8 +188,7 @@ class ResourcesTest : GradlePluginTestBase() { } private fun TestProject.checkAndroidApk(flavor: String, type: String, commonResourcesFiles: Sequence) { - val typeFilePostfix = if (type == "release") "$type-unsigned" else type - val apk = file("build/outputs/apk/$flavor/$type/resources_test-$flavor-$typeFilePostfix.apk") + val apk = file("build/outputs/apk/$flavor/$type/resources_test-$flavor-$type.apk") assert(apk.exists()) ZipFile(apk).let { zip -> commonResourcesFiles.forEach { res -> diff --git a/gradle-plugins/compose/src/test/test-projects/misc/commonResources/build.gradle.kts b/gradle-plugins/compose/src/test/test-projects/misc/commonResources/build.gradle.kts index 392b9a9efc5..b26aa2a7509 100644 --- a/gradle-plugins/compose/src/test/test-projects/misc/commonResources/build.gradle.kts +++ b/gradle-plugins/compose/src/test/test-projects/misc/commonResources/build.gradle.kts @@ -40,7 +40,7 @@ android { } buildTypes { getByName("release") { - isMinifyEnabled = false + initWith(getByName("debug")) } } compileOptions { From 3a3f0e180c8f92163a41c5080858c822c1593284 Mon Sep 17 00:00:00 2001 From: Konstantin Tskhovrebov Date: Mon, 12 Feb 2024 12:55:41 +0100 Subject: [PATCH 6/7] Add kotlin-test to gradle tests for readable test reports and fix closing Zip resource --- gradle-plugins/compose/build.gradle.kts | 1 + .../test/tests/integration/ResourcesTest.kt | 104 ++++++++++-------- 2 files changed, 58 insertions(+), 47 deletions(-) diff --git a/gradle-plugins/compose/build.gradle.kts b/gradle-plugins/compose/build.gradle.kts index e89fdd48f20..85c9df2114b 100644 --- a/gradle-plugins/compose/build.gradle.kts +++ b/gradle-plugins/compose/build.gradle.kts @@ -63,6 +63,7 @@ dependencies { compileOnly(libs.plugin.android) compileOnly(libs.plugin.android.api) + testImplementation(kotlin("test")) testImplementation(gradleTestKit()) testImplementation(kotlin("gradle-plugin-api")) diff --git a/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt b/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt index 911b8b35564..66e1f9dfe26 100644 --- a/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt +++ b/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt @@ -1,14 +1,10 @@ package org.jetbrains.compose.test.tests.integration import org.jetbrains.compose.test.utils.* -import org.jetbrains.compose.test.utils.assertEqualTextFiles -import org.jetbrains.compose.test.utils.assertNotEqualTextFiles -import org.jetbrains.compose.test.utils.checks import org.junit.jupiter.api.Test import java.io.File -import java.util.zip.ZipEntry import java.util.zip.ZipFile -import kotlin.io.path.Path +import kotlin.test.* class ResourcesTest : GradlePluginTestBase() { @Test @@ -36,72 +32,88 @@ class ResourcesTest : GradlePluginTestBase() { file("src/commonMain/composeResources/drawable-ren") ) gradle("generateComposeResClass").checks { - check.logContains(""" + check.logContains( + """ contains unknown qualifier: 'ren'. - """.trimIndent()) + """.trimIndent() + ) } file("src/commonMain/composeResources/drawable-ren").renameTo( file("src/commonMain/composeResources/drawable-rUS-en") ) gradle("generateComposeResClass").checks { - check.logContains(""" + check.logContains( + """ Region qualifier must be declared after language: 'en-rUS'. - """.trimIndent()) + """.trimIndent() + ) } file("src/commonMain/composeResources/drawable-rUS-en").renameTo( file("src/commonMain/composeResources/drawable-rUS") ) gradle("generateComposeResClass").checks { - check.logContains(""" + check.logContains( + """ Region qualifier must be used only with language. - """.trimIndent()) + """.trimIndent() + ) } file("src/commonMain/composeResources/drawable-rUS").renameTo( file("src/commonMain/composeResources/drawable-en-fr") ) gradle("generateComposeResClass").checks { - check.logContains(""" + check.logContains( + """ contains repetitive qualifiers: 'en' and 'fr'. - """.trimIndent()) + """.trimIndent() + ) } file("src/commonMain/composeResources/drawable-en-fr").renameTo( file("src/commonMain/composeResources/image") ) gradle("generateComposeResClass").checks { - check.logContains(""" + check.logContains( + """ Unknown resource type: 'image' - """.trimIndent()) + """.trimIndent() + ) } file("src/commonMain/composeResources/image").renameTo( file("src/commonMain/composeResources/files-de") ) gradle("generateComposeResClass").checks { - check.logContains(""" + check.logContains( + """ The 'files' directory doesn't support qualifiers: 'files-de'. - """.trimIndent()) + """.trimIndent() + ) } file("src/commonMain/composeResources/files-de").renameTo( file("src/commonMain/composeResources/strings") ) gradle("generateComposeResClass").checks { - check.logContains(""" + check.logContains( + """ Unknown resource type: 'strings'. - """.trimIndent()) + """.trimIndent() + ) } file("src/commonMain/composeResources/strings").renameTo( file("src/commonMain/composeResources/string-us") ) gradle("generateComposeResClass").checks { - check.logContains(""" + check.logContains( + """ Forbidden directory name 'string-us'! String resources should be declared in 'values/strings.xml'. - """.trimIndent()) + """.trimIndent() + ) } //restore defaults @@ -161,23 +173,22 @@ class ResourcesTest : GradlePluginTestBase() { checkAndroidApk("full", "release", commonResourcesFiles) val desktopJar = file("build/libs/resources_test-desktop.jar") - assert(desktopJar.exists()) - ZipFile(desktopJar).let { zip -> + assertTrue(desktopJar.exists()) + ZipFile(desktopJar).use { zip -> commonResourcesFiles.forEach { res -> - assert(zip.getEntry(res) != null) + assertNotNull(zip.getEntry(res)) } - assert(zip.getEntry("files/platform.txt") != null) - val text = zip.getInputStream( - zip.getEntry("files/platform.txt") - ).readBytes().decodeToString() - assert(text == "desktop") + val platformTxt = zip.getEntry("files/platform.txt") + assertNotNull(platformTxt) + val text = zip.getInputStream(platformTxt).readBytes().decodeToString() + assertEquals("desktop", text) } val jsBuildDir = file("build/dist/js/productionExecutable") commonResourcesFiles.forEach { res -> - assert(jsBuildDir.resolve(res).exists()) + assertTrue(jsBuildDir.resolve(res).exists()) } - assert(jsBuildDir.resolve("files/platform.txt").readText() == "js") + assertEquals("js", jsBuildDir.resolve("files/platform.txt").readText()) } } @@ -189,18 +200,17 @@ class ResourcesTest : GradlePluginTestBase() { private fun TestProject.checkAndroidApk(flavor: String, type: String, commonResourcesFiles: Sequence) { val apk = file("build/outputs/apk/$flavor/$type/resources_test-$flavor-$type.apk") - assert(apk.exists()) - ZipFile(apk).let { zip -> + assertTrue(apk.exists()) + ZipFile(apk).use { zip -> commonResourcesFiles.forEach { res -> - assert(zip.getEntry(res) != null) + assertNotNull(zip.getEntry(res)) //todo fix duplicate fonts } - assert(zip.getEntry("assets/font/emptyFont.otf") != null) - assert(zip.getEntry("files/platform.txt") != null) - val text = zip.getInputStream( - zip.getEntry("files/platform.txt") - ).readBytes().decodeToString() - assert(text == "android $flavor-$type") + assertNotNull(zip.getEntry("assets/font/emptyFont.otf")) + val platformTxt = zip.getEntry("files/platform.txt") + assertNotNull(platformTxt) + val text = zip.getInputStream(platformTxt).readBytes().decodeToString() + assertEquals("android $flavor-$type", text) } } @@ -208,7 +218,7 @@ class ResourcesTest : GradlePluginTestBase() { fun testUpToDateChecks(): Unit = with(testProject("misc/commonResources")) { gradle("prepareKotlinIdeaImport").checks { check.taskSuccessful(":generateComposeResClass") - assert(file("build/generated/compose/resourceGenerator/kotlin/app/group/resources_test/generated/resources/Res.kt").exists()) + assertTrue(file("build/generated/compose/resourceGenerator/kotlin/app/group/resources_test/generated/resources/Res.kt").exists()) } gradle("prepareKotlinIdeaImport").checks { check.taskUpToDate(":generateComposeResClass") @@ -222,12 +232,12 @@ class ResourcesTest : GradlePluginTestBase() { } gradle("prepareKotlinIdeaImport").checks { check.taskSuccessful(":generateComposeResClass") - assert(!file("build/generated/compose/resourceGenerator/kotlin/app/group/resources_test/generated/resources/Res.kt").exists()) + assertFalse(file("build/generated/compose/resourceGenerator/kotlin/app/group/resources_test/generated/resources/Res.kt").exists()) } gradle("prepareKotlinIdeaImport", "-Pcompose.resources.always.generate.accessors=true").checks { check.taskSuccessful(":generateComposeResClass") - assert(file("build/generated/compose/resourceGenerator/kotlin/app/group/resources_test/generated/resources/Res.kt").exists()) + assertTrue(file("build/generated/compose/resourceGenerator/kotlin/app/group/resources_test/generated/resources/Res.kt").exists()) } modifyText("build.gradle.kts") { str -> @@ -238,7 +248,7 @@ class ResourcesTest : GradlePluginTestBase() { } gradle("prepareKotlinIdeaImport").checks { check.taskUpToDate(":generateComposeResClass") - assert(file("build/generated/compose/resourceGenerator/kotlin/app/group/resources_test/generated/resources/Res.kt").exists()) + assertTrue(file("build/generated/compose/resourceGenerator/kotlin/app/group/resources_test/generated/resources/Res.kt").exists()) } modifyText("build.gradle.kts") { str -> @@ -249,8 +259,8 @@ class ResourcesTest : GradlePluginTestBase() { } gradle("prepareKotlinIdeaImport").checks { check.taskSuccessful(":generateComposeResClass") - assert(!file("build/generated/compose/resourceGenerator/kotlin/app/group/resources_test/generated/resources/Res.kt").exists()) - assert(file("build/generated/compose/resourceGenerator/kotlin/io/company/resources_test/generated/resources/Res.kt").exists()) + assertFalse(file("build/generated/compose/resourceGenerator/kotlin/app/group/resources_test/generated/resources/Res.kt").exists()) + assertTrue(file("build/generated/compose/resourceGenerator/kotlin/io/company/resources_test/generated/resources/Res.kt").exists()) } } @@ -356,7 +366,7 @@ class ResourcesTest : GradlePluginTestBase() { gradle("desktopJar").checks { check.taskSuccessful(":generateStringFiles") check.taskSuccessful(":generateComposeResClass") - assert(file("src/commonMain/composeResources/values/strings.xml").readLines().size == 513) + assertEquals(513, file("src/commonMain/composeResources/values/strings.xml").readLines().size) } } From 73f00e7987abe0e3088a94ee847d8c7192519b70 Mon Sep 17 00:00:00 2001 From: Konstantin Tskhovrebov Date: Mon, 12 Feb 2024 14:34:38 +0100 Subject: [PATCH 7/7] Fix file paths in tests on Windows --- .../jetbrains/compose/test/tests/integration/ResourcesTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt b/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt index 66e1f9dfe26..534216f3ee6 100644 --- a/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt +++ b/gradle-plugins/compose/src/test/kotlin/org/jetbrains/compose/test/tests/integration/ResourcesTest.kt @@ -159,7 +159,7 @@ class ResourcesTest : GradlePluginTestBase() { val commonResourcesDir = file("src/commonMain/composeResources") val commonResourcesFiles = commonResourcesDir.walkTopDown() .filter { !it.isDirectory && !it.isHidden } - .map { it.relativeTo(commonResourcesDir).path } + .map { it.relativeTo(commonResourcesDir).invariantSeparatorsPath } gradle("build").checks { check.taskSuccessful(":copyDemoDebugFontsToAndroidAssets")