blob: 2df40ead0c045be2acc4e17e8c529051140073b7 [file] [log] [blame]
/*
* Copyright (C) 2018 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.databinding
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.build.gradle.integration.common.runner.FilterableParameterized
import com.android.build.gradle.integration.common.truth.ApkSubject.assertThat
import com.android.build.gradle.integration.common.truth.GradleTaskSubject
import com.android.build.gradle.integration.common.utils.TestFileUtils
import com.android.build.gradle.options.BooleanOption
import com.android.testutils.apk.Apk
import org.hamcrest.CoreMatchers
import org.hamcrest.MatcherAssert
import org.junit.Assert.assertNull
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import java.io.File
@RunWith(FilterableParameterized::class)
class DataBindingMultiModuleTest(useAndroidX: Boolean) {
private val bindingPkg = if (useAndroidX) {
"androidx.databinding"
} else {
"android.databinding"
}
@Rule
@JvmField
val project: GradleTestProject = GradleTestProject.builder()
.fromTestProject("databindingMultiModule")
.addGradleProperties(BooleanOption.USE_ANDROID_X.propertyName + "=" + useAndroidX)
.addGradleProperties(BooleanOption.ENABLE_JETIFIER.propertyName + "=" + useAndroidX)
.create()
@Test
fun checkBRClasses() {
// use release to get 1 classes.dex instead of many
project.executor().run("clean", "assembleRelease")
val apk = getApk()
assertThat(apk).exists()
assertBRs("_all", "inheritedInput", "input", "appInput")
// add a bindable to the inherited
val newBindable = project
.file("inherited/src/main/java/android/databinding/multimodule/inherited/NewBindable.java")
newBindable
.writeText(
"""
package android.databinding.multimodule.inherited.NewBindable;
import $bindingPkg.Bindable;
import $bindingPkg.BaseObservable;
public class NewBindable extends BaseObservable {
@Bindable
public int newBindableVar;
}
""".trimIndent()
)
project.executor().run("assembleRelease")
assertBRs("_all", "inheritedInput", "input", "newBindableVar", "appInput")
// rename an input field
TestFileUtils.searchAndReplace(
project.file("inherited/src/main/res/layout/inherited_main.xml"),
"inheritedInput",
"inheritedInput2"
)
project.executor().run("assembleRelease")
assertBRs("_all", "inheritedInput2", "input", "newBindableVar", "appInput")
newBindable.delete()
project.executor().run("assembleRelease")
assertBRs("_all", "inheritedInput2", "input", "appInput")
}
@Test
fun checkAdaptersAreNotInherited() {
createAdapterInInherited("customSetText")
TestFileUtils.searchAndReplace(
project.file("library/src/main/res/layout/activity_main.xml"),
"android:text=\"@{input}\"",
"app:customSetText=\"@{input}\""
)
val result = project.executor().run("assembleRelease")
assertNull(result.exception)
// now try to use it in the app, should fail
val appLayout = project.file("app/src/main/res/layout/app_layout.xml")
appLayout.mkdirs()
TestFileUtils.searchAndReplace(
appLayout,
"android:text=\"@{appInput}\"",
"app:customSetText=\"@{appInput}\""
)
val result2 = project.executor().expectFailure().run("assembleRelease")
MatcherAssert.assertThat(
result2.failureMessage ?: "",
CoreMatchers.containsString("Cannot find a setter for <android.widget.TextView app:customSetText> that accepts parameter type 'java.lang.String'")
)
}
@Test
fun checkCompilationAvoidanceOnLayoutChange() {
project.executor().run(APP_COMPILE_JAVA_TASK)
TestFileUtils.searchAndReplace(
project.file("inherited/src/main/res/layout/inherited_main.xml"),
"android:text=\"@{inheritedInput}\"",
"android:text=\"@{inheritedInput + inheritedInput}\""
)
val result = project.executor().run(APP_COMPILE_JAVA_TASK)
GradleTaskSubject.assertThat(result.getTask(APP_COMPILE_JAVA_TASK)).wasUpToDate()
}
@Test
fun checkCompilationAvoidanceOnAdapterChange() {
project.executor().run(APP_COMPILE_JAVA_TASK)
createAdapterInInherited("setMyText")
TestFileUtils.searchAndReplace(
project.file("inherited/src/main/res/layout/inherited_main.xml"),
"android:text=\"@{inheritedInput}\"",
"app:setMyText=\"@{inheritedInput}\""
)
val result = project.executor().run(APP_COMPILE_JAVA_TASK)
GradleTaskSubject.assertThat(result.getTask(LIBRARY_COMPILE_JAVA_TASK)).didWork()
GradleTaskSubject.assertThat(result.getTask(APP_COMPILE_JAVA_TASK)).wasUpToDate()
}
@Test
fun checkCompilationAvoidanceOnDummyAdapterChange() {
val adapterFile = createAdapterInInherited("setMyText")
project.executor().run(APP_COMPILE_JAVA_TASK)
TestFileUtils.searchAndReplace(adapterFile,
"public class NewAdapter {",
"public class //dummy comment\nNewAdapter {"
)
val result = project.executor().run(APP_COMPILE_JAVA_TASK)
GradleTaskSubject.assertThat(result.getTask(LIBRARY_COMPILE_JAVA_TASK)).wasUpToDate()
GradleTaskSubject.assertThat(result.getTask(APP_COMPILE_JAVA_TASK)).wasUpToDate()
}
private fun createAdapterInInherited(attributeName: String) : File {
val newAdapter = project
.file("inherited/src/main/java/android/databinding/multimodule/inherited/NewAdapter.java")
newAdapter
.writeText(
"""
package android.databinding.multimodule.inherited;
import $bindingPkg.BindingAdapter;
import android.widget.TextView;
public class NewAdapter {
@BindingAdapter("$attributeName")
public static void customSetText(TextView textView, String customText) {}
}
""".trimIndent()
)
return newAdapter
}
private fun assertBRs(vararg fields: String) {
val apk = getApk()
assertThat(apk)
.hasMainDexFile()
.that().run {
BR_CLASSES.forEach { brClass ->
containsClass(brClass)
.that()
.hasExactFields(fields.toSet())
}
}
}
private fun getApk(): Apk {
return project
.getSubproject("app")
.getApk(GradleTestProject.ApkType.RELEASE)
}
companion object {
private const val LIB_PKG = "Landroid/databinding/multimodule/library"
private const val APP_PKG = "Landroid/databinding/multimodule/app"
private const val INHERITED_PKG = "Landroid/databinding/multimodule/inherited"
private val ALL_PKGS = arrayOf(LIB_PKG, APP_PKG, INHERITED_PKG)
private val BR_CLASSES = ALL_PKGS.map { "$it/BR;" }.toTypedArray()
private const val APP_COMPILE_JAVA_TASK = ":app:compileReleaseJavaWithJavac"
private const val LIBRARY_COMPILE_JAVA_TASK = ":library:compileReleaseJavaWithJavac"
@Parameterized.Parameters(name = "useAndroidX_{0}")
@JvmStatic
fun params() = listOf(true, false)
}
}