targetProjectPath in android TestExtension

Hi,

How we can set the targetProjectPath property in android test extension during configuration. W are creating a custom plugin which takes a target path property from consumer. If we dont set targetProject path, config fails as its not a property field in the Test extension rather a variable

Wondering whats the best way to handle this, afterEvaluate works but i need to set some default targetProjectPath, which i am trying to avoid setting one

Here is the sample code, that i am using

class CustomPlugin : Plugin<Project> {
    override fun apply(project: Project) {
        val uiTestConfigExtension =
            project.extensions.create("customExtenstion", TestModuleExtension::class.java)
                .apply { targetApp.convention(":sampleApp") }

        with(project) {
            with(pluginManager) {
                apply("com.android.test")
            }
        }
        extensions.getByType<TestExtension>().apply {
            targetProjectPath = "some default"
            // want to set the target path during configuration phase
            afterEvaluate {
                targetProjectPath = uiTestConfigExtension.targetApp.get()
            }
        }
    }
}

Appreciate any inputs or pointers on how we can set the targetProjectPath dynamically from consumer end.

afterEvaluate works

You mean it works sometimes under certain conditions. afterEvaluate is practically never a solution, but duct tape. The main effect of afterEvaluate is to introduce timing problems, ordering problems, and race conditions. It is like using SwingUtilities.invokeLater or Platform.runLater to “fix” a GUI problem. It just does symptom treatment, delaying the problem to a later, harder to reproduce, harder to debug, and harder to fix point in time.

As targetProjectPath is a primitive property and not a Property, I’d remove targetApp from the TestModuleExtension and instead add a function setTargetApp where you give the value as argument, then you can directly set the value there.

Thanks @Vampire for taking the time to answer.

I recall reading in an earlier post about the suggested solution. I attempted to implement it, but with not much luck. As the targetProjectPath is a property, my understanding is its evaluated in the initialisation phase and android test plugin adds a project dependency based on this.

I would like to defer this dependency until the consumer project provides it. Can you elaborate on how adding a function would defer this dependency?.

Appreciate your help here.

I have no idea what Android Gradle Plugin does.
But if using afterEvaluate { ... } works, then a function would also work, as the function call is happening before the afterEvaluate evaluation happens.

So what is your problem with that approach?
Remove the property from the extension, add a function with targetApp parameter, in the function body set the targetProjectPath to the argument.
The consumer will then call that function instead of setitng the property.

1 Like

Thanks again @Vampire for taking the time to response.

This is the working code

targetPath = ":default"
afterEvaluate {
  targetPath = if (hasProperty("target"))  "targetA" else "targetB"
}

With the above approach, default project was added to the dependency list initially and then during configuration based on condition either targetA or targetB is getting added. To speed up, i was looking for a way to skip the default project from getting set. If i dont set any, then the initialisation phase will fail as the plugin expects a value, i was wondering is there a way to overcome this problem.

Hope this clarifies the issue i was trying to solve.

Not really.
My answer stays the same.
If you think I still did not understand what you mean, you should probably provide an MCVE

I misunderstood the earlier suggestion on the function. I was able to fix it with the suggested approach.

interface TestModuleExtension {
    fun setTargetApp(targetApp: String, project: Project) {
        with(project) {
            with(pluginManager) {
                apply("com.android.test")
            }
        }
        extensions.getByType<TestExtension>().apply {
            targetProjectPath = targetApp
        }
    }
}

class CustomPlugin : Plugin<Project> {
    override fun apply(project: Project) {
        project.extensions.create("customExtenstion", TestModuleExtension::class.java)
    }
}

That’s not exactly what I would suggest.

You should not give the project as argument.
Just either give it to the extension when you create it, or simple let it be injected by Gradle.
I’m also not sure it is a good idea to apply the plugin in that method.
I’d keep the plugin application in your apply method, before creating the extension.