Issues running Android Espresso task with gradle lock strict mode

When using configuration locking mode DEFAULT or LENIENT able to run Android UI test
We have enabled gradle configuration cache.

I am using gradle version gradle 8.10.2, and Android Gradle plugin version 8.2.2

However, when I set configuration locking mode to STRICT I run into error shown below

Configuration cache state could not be cached: field `__additionalTestOutputPlugin__` of `com.android.build.gradle.internal.testing.utp.UtpDependencies` bean found in field `__utpDependencies__` of `com.android.build.gradle.internal.tasks.DeviceProviderInstrumentTestTask$TestRunnerFactory` bean found in field `__testRunnerFactory__` of task `:my_app:connectedDevDebugAndroidTest` of type `com.android.build.gradle.internal.tasks.DeviceProviderInstrumentTestTask`: error writing value of type 'org.gradle.api.internal.file.collections.DefaultConfigurableFileCollection'
> Could not resolve all dependencies for configuration ':my_app:_internal-unified-test-platform-android-test-plugin-host-additional-test-output'.
   > Locking strict mode: Configuration ':my_app:_internal-unified-test-platform-android-test-plugin-host-additional-test-output' is locked but does not have lock state.

I attempted to add following as suggested here, but that didn’t help.

https://docs.gradle.org/current/userguide/dependency_locking.html#sec:fine-tuning-dependency-locking-behaviour-with-lock-mode

notCompatibleWithConfigurationCache("com.android.build.gradle.internal.testing.utp.UtpDependencies")

In the task below

tasks.register<Exec>("lockAllModulesDependencies") {
  notCompatibleWithConfigurationCache("com.android.build.gradle.internal.testing.utp.UtpDependencies")
  val listOfProjects = subprojects.map { ":${it.name}:dependencies" }.joinToString(" ")
  val genGradleLock = "./gradlew $listOfProjects --write-locks; ./gradlew :my_app:dependencies --write-locks"
  commandLine("/bin/sh", "-c", genGradleLock)
}

What am I doing wrong?

The error has not really anything to do with configuration cache, so notCompatibleWithConfigurationCache does not change anything.

As the lower-most message says,
you enabled strict locking,
you enabled locking for the configuration _internal-unified-test-platform-android-test-plugin-host-additional-test-output
but in your lock file you do not have any lock information about that configuration.

Either do not lock that configuration, or update the lock information for that configuration as documented on the dependency locking documentation page.

Thanks for responding…

Sorry, I don’t explicitly lock configuration for _internal-unified-test-platform-android-test-plugin-host-additional-test-output

When generating lockfile I am executing task - lockAllModulesDependencies - so not 100% sure I am locking configuration - would really appreciate bit more pointer.

I don’t understand fully
lock information for that configuration as documented on the dependency locking documentation page. can you please point which configuration I should skip?

Thanks!

When generating lockfile I am executing task - lockAllModulesDependencies - so not 100% sure I am locking configuration - would really appreciate bit more pointer.

That does only update the locking information and only for the configurations that are actually resolved during the execution afair.
Which configurations you lock is configured in another place.
You probably somewhere call lockAllConfigurations() which indeed locks all configurations including the mentioned one.
Probably your lock-updating call does not trigger the configuration in question to be resolved and thus no lock information be written for it.

Yes indeed I have lockAllConfiguratoin in my app build.gradle.

I am at bit at complete loss as to which configuration should be generating lockfile for or ignoring lockfile. Is there some output from ./gradlew dependencies or something I should be sharing here?

I’m not really too familiar with dependency locking, as I never use dynamic version or version ranges and thus never use dependency locking.
But as I said, when writing the lock file with --write-locks it writes locks for the configurations that are resolved during that execution.
If you have strict lock mode and it complains that you do not have lock state for _internal-unified-test-platform-android-test-plugin-host-additional-test-output that means that _internal-unified-test-platform-android-test-plugin-host-additional-test-output was not resolved when you run with --write-locks but is currently resolved and thus misses lock information.

That is the part that is confusing we don’t have this task _internal-unified-test-platform-android-test-plugin-host-additional-test-output

But when I try to generate lock file for _internal-unified-test-platform-android-test-plugin-host-additional-test-output I see this.

./gradlew --no-configuration-cache _internal-unified-test-platform-android-test-plugin-host-additional-test-output --write-locks
Configuration on demand is an incubating feature.

> Configure project :my_app
FAILURE: Build failed with an exception.

* What went wrong:
Task '_internal-unified-test-platform-android-test-plugin-host-additional-test-output' not found in root project 'android' and its subprojects.

I have no idea where this task _internal-unified-test-platform-android-test-plugin-host-additional-test-output is coming from!!

That is the part that is confusing we don’t have this task _internal-unified-test-platform-android-test-plugin-host-additional-test-output

It’s not a task, it’s a configuration.

My bad for using task v/s configuration.

Okay taks that triggers UI tests is :my_app:connectedDevDebugAndroidTest - my several attempt to “exclude” this from dependencyLocking have failed - I have reverted to using “lockMode.DEFAULT”.

Wish I understood this almost magic better!!!

dependencyLocking {
    lockAllConfigurations()
    lockMode.set(LockMode.DEFAULT)
}

Thanks Björn for your responses!

You do not exclude a task from dependency locking, but a configuration.
If you want to not lock that configuration but all others, you probably have to not use lockAllConfigurations() but something like

configurations.named {
    it != "_internal-unified-test-platform-android-test-plugin-host-additional-test-output"
}.configureEach {
    resolutionStrategy.activateDependencyLocking()
}
1 Like

Thanks for your patience with this. Finally came up witht this that works and now locking configuration for configurations that I need.

dependencyLocking {
    def containsPatterns = [
            "androidtest",
            "unittest",
            "test-plugin",
            "test-platform"
    ]

    def startsWithPatterns = [
            "test",
            "detekt",
            "ktlint"
    ]
    configurations.configureEach {
        def lowerCaseName = name.toLowerCase()
        def shouldExclude = containsPatterns.any { pattern -> lowerCaseName.contains(pattern) } ||
                startsWithPatterns.any { pattern -> lowerCaseName.startsWith(pattern) }
        if (shouldExclude) {
            resolutionStrategy.deactivateDependencyLocking()
        } else {
            resolutionStrategy.activateDependencyLocking()
            lockMode.set(LockMode.STRICT)
        }
    }
}
1 Like