Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TestKit failure: cannot find extension of type AppExtension #11338

Closed
autonomousapps opened this issue Nov 12, 2019 · 14 comments
Closed

TestKit failure: cannot find extension of type AppExtension #11338

autonomousapps opened this issue Nov 12, 2019 · 14 comments

Comments

@autonomousapps
Copy link
Contributor

I am developing a Gradle plugin that reacts to various Android plugins (com.android.application and com.android.library) in order to report unused dependencies. You can find it here. I have added a stub TestKit functional test. I have actually been able to "build" my stub Android project with TestKit, but the moment I apply my plugin-under-test to the plugins {} block in this stub project, there is a failure at TestKit-build time. More information in the "Context" section below.

Expected Behavior

The functional test should pass.

Current Behavior

The test fails because it cannot find AppExtension. I presume it is failing at this line, but the failure stacktrace is too opaque to be certain.

Context

The issue is affecting me because I cannot write functional tests to validate end-to-end behavior and defend against regressions.

The failure is:

  • What went wrong:
    A problem occurred configuring project ':app'.

Extension of type 'AppExtension' does not exist. Currently registered extension types: [ExtraPropertiesExtension, DefaultArtifactPublicationSet, ReportingExtension, SourceSetContainer, JavaPluginExtension, NamedDomainObjectContainer, BaseAppModuleExtension, KotlinAndroidProjectExtension, KotlinTestsRegistry]

This is nonsensical, since the com.android.application plugin has been applied, and AppExtension must exist at this point. Please note that manual tests of this plugin have succeeded, and the Android "app" defined in the TestKit context "builds" just fine. (You can remove the line id('com.autonomousapps.dependency-analysis') and re-run the test, and it will pass.)

This issue might be tangentially related to this issue. But please note, I have encountered that particular issue before, and you can see the workaround here.

Steps to Reproduce

  1. Clone this repo.
  2. Ensure your build environment is set up to build Android apps (you will need the Android SDK and build-tools).
  3. Run ./gradlew funcTest
  4. Observe the failure.

Your Environment

Build scan URL: https://scans.gradle.com/s/zwg5yeodiqauss (but I cannot view it)

@autonomousapps
Copy link
Contributor Author

I've attempted a couple of workaround based on a conversation in the community slack.

This is the original code

the<AppExtension>().applicationVariants.all {
  val androidClassAnalyzer = AppClassAnalyzer(this@analyzeAndroidApplicationDependencies, name)
  analyzeAndroidDependencies(androidClassAnalyzer)
}

it uses the Kotlin DSL.

I tried replacing with

(extensions.getByName("android") as AppExtension).applicationVariants.all {
  val androidClassAnalyzer = AppClassAnalyzer(this@analyzeAndroidApplicationDependencies, name)
  analyzeAndroidDependencies(androidClassAnalyzer)
}

and got the error:

com.android.build.gradle.internal.dsl.BaseAppModuleExtension_Decorated cannot be cast to com.android.build.gradle.AppExtension

Which is ridiculous, as BaseAppModuleExtension extends AppExtension.

I then tried replacing with

(extensions.getByName("android") as BaseAppModuleExtension).applicationVariants.all {
  val androidClassAnalyzer = AppClassAnalyzer(this@analyzeAndroidApplicationDependencies, name)
  analyzeAndroidDependencies(androidClassAnalyzer)
}

and got the error

com.android.build.gradle.internal.dsl.BaseAppModuleExtension_Decorated cannot be cast to com.android.build.gradle.internal.dsl.BaseAppModuleExtension

and now I don't even.

@autonomousapps
Copy link
Contributor Author

Can I get some eyes on this? It's making it impossible to write any functional tests for a plugin designed to work on Android projects. I provided a build scan and a reproducer with clear steps. What else can I do?

@vlsi
Copy link
Contributor

vlsi commented Dec 18, 2019

@autonomousapps , are you 100% sure the extension class is valid?

The exception message says "Extension of type 'AppExtension' does not exist", and it does say "KotlinAndroidProjectExtension" is present.

Are you sure the very same build succeeds and AppExtension indeed is present?

PS. You can "wait" for both plugins by using nested calls like

plugins.withId("...kotlin...") {
    plugins.withId("...android...") {
        /* this will be executed when both kotlin and android are activated*/
    }
}

@autonomousapps
Copy link
Contributor Author

autonomousapps commented Dec 19, 2019 via email

@autonomousapps
Copy link
Contributor Author

I found a workaround, but it's highly unsatisfying. I still think there's a bug in TestKit somewhere. The workaround suggests that my plugin and AGP are loaded in different classloaders and can't see each other. If I add this to my functional test:

        val buildSrcDir = projectDir.resolve("buildSrc")
        buildSrcDir.mkdirs()
        buildSrcDir.resolve("settings.gradle").writeText("")
        buildSrcDir.resolve("build.gradle").writeText("""
            repositories {
                jcenter()
                google()
                gradlePluginPortal()
            }
            dependencies {
                implementation("com.android.tools.build:gradle:3.5.3")
                implementation("gradle.plugin.com.autonomousapps:dependency-analysis-gradle-plugin:0.10.0")
            }
        """.trimIndent())

then it works. In other words, I'm adding a buildSrc dir to my fake project-under-test in order to force AGP and my plugin to share a classloader.

And actually, I'm not sure what the implications are as I iterate on my plugin. Will it actually use my plugin-under-test, or will it use the binary version it is downloading from the gradle plugin portal?

@autonomousapps
Copy link
Contributor Author

autonomousapps commented Dec 22, 2019

Unfortunately, it's really not a great workaround. It uses the version of the code hosted by the plugin portal, so I can't use it locally without publishing first. May as well just test manually.

@autonomousapps
Copy link
Contributor Author

Finally able to really resolve this. I had

tasks.withType<PluginUnderTestMetadata>().configureEach {
    pluginClasspath.from(configurations.compileOnly)
}

above my dependencies {} block. When I moved it to below, everything worked fine. So, that bit of code was eager and not lazy as I implicitly expected.

Suggestion: the compileOnly configuration should be automatically added to the test/TAPI classpath, so users aren't forced to auto-wire it in this undocumented way. We need to add things like AGP to the compileOnly configuration so we're not forcing our version choices on the users of our plugins.

@CristianGM
Copy link
Contributor

@autonomousapps shouldn't you update the issue saying the workaround didn't work at all?

@autonomousapps
Copy link
Contributor Author

That's what #11338 (comment) says.

@CristianGM
Copy link
Contributor

But your last comment says opposite
#11338 (comment)

@autonomousapps
Copy link
Contributor Author

That one does in fact work. It was an ordering issue. I was trying to add the compileOnly dependencies to the test kit classpath, but it was empty, because I did it BEFORE my dependencies declarations.

@CristianGM
Copy link
Contributor

Then we should CLOSE this issue.

I should open a new one with the real issue that is not being able to test a plugin against multiple versions of the Android Gradle Plugin, which was my issue and as it fails with the same error I thought we where talking about the same.
Maybe for the reproducer you could already show it because you have the same issue and you fixed it like me using project properties to test agains multiple versions.

@stale
Copy link

stale bot commented Jan 15, 2021

This issue has been automatically marked as stale because it has not had recent activity. Given the limited bandwidth of the team, it will be automatically closed if no further activity occurs. If you're interested in how we try to keep the backlog in a healthy state, please read our blog post on how we refine our backlog. If you feel this is something you could contribute, please have a look at our Contributor Guide. Thank you for your contribution.

@stale stale bot added the stale label Jan 15, 2021
@stale
Copy link

stale bot commented Feb 5, 2021

This issue has been automatically closed due to inactivity. If you can reproduce this on a recent version of Gradle or if you have a good use case for this feature, please feel free to reopen the issue with steps to reproduce, a quick explanation of your use case or a high-quality pull request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants