blob: 14e58b25863a23330c16d1b3e83b3c7688ce5ab4 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.build.gradle.integration.application
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.build.gradle.integration.common.fixture.app.MinimalSubProject
import com.android.build.gradle.integration.common.fixture.app.MultiModuleTestProject
import com.android.build.gradle.integration.common.truth.TruthHelper.assertThatApk
import com.android.build.gradle.internal.res.shrinker.DummyContent
import com.android.build.gradle.options.BooleanOption
import com.android.build.gradle.options.OptionalBooleanOption
import com.android.builder.internal.aapt.v2.Aapt2RenamingConventions
import com.android.tools.build.apkzlib.zip.ZFile
import com.android.utils.FileUtils
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
import java.io.File
class CompileLibraryResourcesTest {
private val library = MinimalSubProject.lib("com.example.library")
.withFile(
"src/main/res/drawable-v26/ic_launcher_background.xml",
"""<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:drawable="@drawable/button"/>
</layer-list>
""".trimIndent()
)
.withFile(
"src/main/res/layout/layout_random_name.xml",
"""<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_x="10px"
android:layout_y="110px"
android:text="test"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<EditText
android:layout_x="150px"
android:layout_y="100px"
android:width="150px"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
""".trimIndent()
)
.withFile(
"src/main/res/drawable-v26/button.xml",
"""<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
</vector>
""".trimIndent()
).withFile(
"src/main/res/values-v28/strings.xml",
"""<resources>
<string name="foo">publishedLib</string>
<string name="my_version_name">1.0</string>
</resources>""".trimIndent()
).withFile(
"src/main/res/raw/me.raw",
"test"
)
private val app = MinimalSubProject.app("com.example.app")
.appendToBuild(
"""dependencies { api project(':library') }
android {
buildTypes {
release {
shrinkResources true
minifyEnabled true
}
}
}
"""
.trimIndent()
)
.withFile(
"src/main/res/values/strings.xml",
"""<resources>
<string name="mystring">@string/foo</string>
</resources>"""
)
.withFile(
"src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
"""<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
</adaptive-icon>
""".trimIndent()
)
.withFile(
"src/main/AndroidManifest.xml",
"""<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app" >
<application android:label="app_name" android:icon="@mipmap/ic_launcher">
<activity android:name="MainActivity"
android:label="app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>""".trimIndent()
)
private val testApp =
MultiModuleTestProject.builder()
.subproject(":library", library)
.subproject(":app", app)
.build()
@get:Rule
val project = GradleTestProject.builder().fromTestApp(testApp).create()
@Test
fun testResourcesAreCompiledAndProcessed() {
project.executor().with(BooleanOption.PRECOMPILE_DEPENDENCIES_RESOURCES, true)
.run(":app:assembleDebug")
checkCompiledLibraryResourcesDir(
setOf(
Aapt2RenamingConventions.compilationRename(
File(File("layout"), "layout_random_name.xml")
),
Aapt2RenamingConventions.compilationRename(
File(File("drawable-anydpi-v26"), "button.xml")
),
Aapt2RenamingConventions.compilationRename(
File(File("drawable-v26"), "ic_launcher_background.xml")
),
Aapt2RenamingConventions.compilationRename(
File(File("raw"), "me.raw")
)
)
)
checkOnlyValuesWasMerged()
val apk = project.getSubproject("app").getApk(GradleTestProject.ApkType.DEBUG)
// check resources are added to the final apk
assertThatApk(apk).containsResource("raw/me.raw")
assertThatApk(apk).containsResource("layout/layout_random_name.xml")
assertThatApk(apk).containsResource("drawable-v26/ic_launcher_background.xml")
assertThatApk(apk).containsResource("drawable-anydpi-v26/button.xml")
assertThatApk(apk).containsResource("mipmap-anydpi-v26/ic_launcher.xml")
}
@Test
fun testIncrementalResourceChange() {
project.executor().with(BooleanOption.PRECOMPILE_DEPENDENCIES_RESOURCES, true)
.run(":app:assembleDebug")
val filesLastModified = FileUtils.join(
project.getSubproject(":library").intermediatesDir,
"compiled_local_resources",
"debug",
"out"
).listFiles()!!.sorted().map { it.lastModified() }
// change me.raw
FileUtils.writeToFile(
FileUtils.join(
project.getSubproject(":library").projectDir,
"src",
"main",
"res",
"raw",
"me.raw"
), "test2"
)
project.executor().with(BooleanOption.PRECOMPILE_DEPENDENCIES_RESOURCES, true)
.run(":app:assembleDebug")
FileUtils.join(
project.getSubproject(":library").intermediatesDir,
"compiled_local_resources",
"debug",
"out"
).listFiles()!!.sorted().forEachIndexed { index, file ->
// Check only the raw file was updated
if (file.name.startsWith("raw_me")) {
assertThat(file.lastModified()).isNotEqualTo(filesLastModified[index])
} else {
assertThat(file.lastModified()).isEqualTo(filesLastModified[index])
}
}
// Remove ic_launcher_background
FileUtils.deleteIfExists(
FileUtils.join(
project.getSubproject(":library").projectDir,
"src",
"main",
"res",
"drawable-v26",
"ic_launcher_background.xml"
)
)
FileUtils.writeToFile(
FileUtils.join(
project.getSubproject(":app").projectDir,
"src",
"main",
"res",
"mipmap-anydpi-v26",
"ic_launcher.xml"
), """<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
</adaptive-icon>
""".trimIndent()
)
project.executor().with(BooleanOption.PRECOMPILE_DEPENDENCIES_RESOURCES, true)
.run(":app:assembleDebug")
// check the file doesn't exist in the compiled library resources
checkCompiledLibraryResourcesDir(
setOf(
Aapt2RenamingConventions.compilationRename(
File(File("layout"), "layout_random_name.xml")
),
Aapt2RenamingConventions.compilationRename(
File(File("drawable-anydpi-v26"), "button.xml")
),
Aapt2RenamingConventions.compilationRename(
File(File("raw"), "me.raw")
)
)
)
// check the resource doesn't exist in the final apk
val apk = project.getSubproject("app").getApk(GradleTestProject.ApkType.DEBUG)
assertThatApk(apk).doesNotContainResource("drawable-v26/ic_launcher_background.xml")
}
@Test
fun testIntegrationWithResourceShrinker() {
project.executor().with(BooleanOption.PRECOMPILE_DEPENDENCIES_RESOURCES, true)
// http://b/149978740
.with(BooleanOption.INCLUDE_DEPENDENCY_INFO_IN_APKS, false)
.with(OptionalBooleanOption.INTERNAL_ONLY_ENABLE_R8, true)
.run(":app:assembleRelease")
val compressed = project.getSubproject(":app").getIntermediateFile(
"shrunk_processed_res",
"release",
"resources-release-stripped.ap_"
)
assertThat(compressed.isFile).isTrue()
ZFile.openReadOnly(compressed).use {
assertThat(it.get("res/layout/layout_random_name.xml")!!.read()).isEqualTo(
DummyContent.TINY_BINARY_XML
)
assertThat(it.get("res/drawable-anydpi-v26/button.xml")!!.read()).isNotEqualTo(
DummyContent.TINY_BINARY_XML
)
assertThat(it.get("res/drawable-v26/ic_launcher_background.xml")!!.read())
.isNotEqualTo(
DummyContent.TINY_BINARY_XML
)
}
}
private fun checkCompiledLibraryResourcesDir(expected: Set<String>) {
val compiledLibraryResourcesDir = FileUtils.join(
project.getSubproject(":library").intermediatesDir,
"compiled_local_resources",
"debug",
"out"
)
assertThat(compiledLibraryResourcesDir.listFiles()!!.map { it.name }.toSet()).containsExactlyElementsIn(
expected
)
}
private fun checkOnlyValuesWasMerged() {
val mergedResDir = FileUtils.join(
project.getSubproject(":app").intermediatesDir,
"res",
"merged",
"debug"
)
assertThat(mergedResDir.listFiles()!!.map { it.name }.toSet()).containsExactlyElementsIn(
setOf(
Aapt2RenamingConventions.compilationRename(
File(File("values-v28"), "values-v28.xml")
),
Aapt2RenamingConventions.compilationRename(
File(File("values"), "values.xml")
),
Aapt2RenamingConventions.compilationRename(
File(File("mipmap-anydpi-v26"), "ic_launcher.xml")
)
)
)
}
}