-
Notifications
You must be signed in to change notification settings - Fork 232
Description
Description
The Paparazzi plugin configures the test tasks with a bunch of paths and those paths are set up as absolute paths. system properties are part of the cache key so if CI writes remote build cache entries while having the repository checked out in a directory like /tmp/my-repository
and local machines have it in ~/Projects/my-repository
the cache key of test tasks is different resulting in a cache miss.
Steps to Reproduce
The problem can be reproduced by updating the unit test app.cash.paparazzi.gradle.PaparazziPluginTest.cacheable()
to run the second run in a different directory. In that case assertions that the task came from cache fail
@Test
fun cacheable() {
val fixtureRoot = File("src/test/projects/cacheable")
// Also validate remote cache by running the test in two separate directories that use the same build cache
val secondFixturesRoot = fixtureRoot.parentFile.resolve("cacheable-2").registerForDeletionOnExit()
fixtureRoot.copyRecursively(secondFixturesRoot)
fixtureRoot.resolve("build-cache").registerForDeletionOnExit()
val firstRun = gradleRunner
.withArguments("testDebug", "--build-cache", "--stacktrace")
.runFixture(fixtureRoot) { build() }
with(firstRun.task(":preparePaparazziDebugResources")) {
assertThat(this).isNotNull()
assertThat(this!!.outcome).isNotEqualTo(FROM_CACHE)
}
with(firstRun.task(":testDebugUnitTest")) {
assertThat(this).isNotNull()
assertThat(this!!.outcome).isNotEqualTo(FROM_CACHE)
}
val secondRun = gradleRunner
.withArguments("testDebug", "--build-cache", "--stacktrace")
.runFixture(secondFixturesRoot) { build() }
with(secondRun.task(":preparePaparazziDebugResources")) {
assertThat(this).isNotNull()
assertThat(this!!.outcome).isEqualTo(FROM_CACHE)
}
with(secondRun.task(":testDebugUnitTest")) {
assertThat(this).isNotNull()
assertThat(this!!.outcome).isEqualTo(FROM_CACHE)
}
}
Expected behavior
A second run without input changes in a project that is in a different directory should come from cache. The recommended way of setting up remote build cache is to store cache entries on CI so that local dev machines can use them. If there are absolute paths in the cache key those local machines would never be able to use them
Workaround
I was able to work around this by adding the following gradle logic
androidComponents {
onVariants { variant ->
variant.hostTests.forEach { (_, hostTest) ->
hostTest.configureTestTask { testTask ->
testTask.systemProperties.toMap().forEach { (key, value) ->
if (key.toString().startsWith("paparazzi.") && value is String && value.startsWith(projectDir.absolutePath)) {
testTask.systemProperty(key, value.replace(projectDir.absolutePath, "."))
}
}
testTask.systemProperty("paparazzi.artifacts.cache.dir", "./gradle-user-home")
}
}
}
}
and initialize paparazzi as follows
private val gradleUserHome = File(
System.getenv("GRADLE_USER_HOME")
?: System.getProperty("user.home")?.plus(File.separator)?.plus(".gradle")
?: error("Could not determine gradle user home")
)
private val paparazzi = Paparazzi(
environment = detectEnvironment().run {
copy(
localResourceDirs = localResourceDirs.map { asCanonicalPath(it) },
moduleResourceDirs = moduleResourceDirs.map { asCanonicalPath(it) },
libraryResourceDirs = libraryResourceDirs.map { asCanonicalPath(it) },
allModuleAssetDirs = allModuleAssetDirs.map { asCanonicalPath(it) },
libraryAssetDirs = libraryAssetDirs.map { asCanonicalPath(it) },
appTestDir = File(appTestDir).canonicalPath
)
},
)
private fun asCanonicalPath(path: String) = when {
path.startsWith("./gradle-user-home/") -> gradleUserHome.resolve(path.removePrefix("./gradle-user-home/")).canonicalPath
path.startsWith("./") -> File(path).canonicalPath
else -> path
}
Additional information:
- Paparazzi Version: 1.3.5
- OS: MacOS
- Compile SDK: 35
- Gradle Version: 8.10.2
- Android Gradle Plugin Version: 8.8.0