'com.android.build.gradle.LibraryExtension_Decorated cannot be cast to (.....).BaseExtension
Objective : I have developed Test for CodeCoveragePluginForAndroidConnectedTest .
Steps to reproduce error : ./gradlew clean build
A problem occurred evaluating root project 'QualityToolsTestProject'. > Failed to apply plugin 'kotlin-android'.
class com.android.build.gradle.LibraryExtension_Decorated cannot be cast to class com.android.build.gradle.BaseExtension
(com.android.build.gradle.LibraryExtension_Decorated is in unnamed module of loader
org.gradle.internal.classloader.VisitableURLClassLoader @5a8b9853; com.android.build.gradle.BaseExtension is in unnamed module of loader org.gradle.internal.classloader.VisitableURLClassLoader @7685facc)
Note :
So "Failed to apply plugin 'kotlin-android' in my below test case is because of two different type of gradle implementation ? Project with kts exension (build.gradle.kts)
Test cases with groovy(build.gradle) because in below code i am creating build.gradle for Test cases and corresponding file in vitural gradle memory using groovy.
To understand complete code are below.
Input : TEST_PROJECT_NAME is QualityToolsTestProject in below code
package com.oujji.sdk.android.gradle.quality
import com.ing.sdk.android.gradle.quality.test.*
import org.gradle.testkit.runner.TaskOutcome
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import java.io.File
class CodeCoveragePluginForAndroidConnectedTest : BaseTest() {
private lateinit var buildFile: File
private lateinit var sourceMainDir: File
private lateinit var sourceAndroidTestDir: File
@Before
fun setup() {
testProjectDir.apply {
createGradleProperties()
createSettingsFile(projectName = TEST_PROJECT_NAME)
buildFile = createProjectFile("build.gradle") {
"""
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.0.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.30"
}
}
plugins {
id 'code-coverage'
}
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
repositories {
google()
mavenCentral()
}
android {
compileSdkVersion 29
buildToolsVersion "29.0.2"
defaultConfig {
targetSdkVersion 29
minSdkVersion 21
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
debug {
testCoverageEnabled = true
}
}
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.61"
androidTestImplementation "androidx.test.ext:junit:1.1.1"
androidTestImplementation "androidx.test:runner:1.2.0"
testImplementation "junit:junit:4.12"
}
"""
}
createManifestFile()
sourceMainDir = createSourceMainDirectories()
sourceAndroidTestDir = createSourceAndroidTestDirectories()
}
}
@Test
fun `when applying code coverage for android, code coverage tasks should be added`() {
runBuild("tasks") {
checkOutputContains("coverage - Generate Jacoco coverage reports on test builds.")
checkOutputContains("androidTestCoverageDebug - Generate Jacoco coverage reports on the Debug build.")
checkOutputContains("coveragePrintDebug - Print the code coverage of the Debug build using the coverageDebug report.")
checkOutputContains("coveragePrintRelease - Print the code coverage of the Release build using the coverageRelease report.")
checkOutputContains("coverageVerificationDebug - Run Jacoco coverage verification on the Debug build.")
checkOutputContains("coverageVerificationRelease - Run Jacoco coverage verification on the Release build.")
}
}
@Test
fun `when coverage below default minimum, verify task outcome should be FAILED`() {
// Setup with 40% coverage, default is 50%
buildFile {
"""
codeCoverage {
failOnBelowMinimumCoverage true
}
"""
}
"One".also { className ->
sourceMainDir.createSourceKotlinFile(className = className)
sourceAndroidTestDir.createAndroidTestKotlinFile(testFor = className)
}
"Two".also { className ->
sourceMainDir.createSourceJavaFile(className = className)
sourceAndroidTestDir.createAndroidTestKotlinFile(testFor = className)
}
sourceMainDir.createSourceJavaFile(className = "Three")
sourceMainDir.createSourceKotlinFile(className = "Four")
sourceMainDir.createSourceKotlinFile(className = "Five")
runBuildAndFail("androidTestCoverageDebug") {
assertEquals(TaskOutcome.FAILED, task(":coverageVerificationDebug")?.outcome)
checkOutputContains("Code coverage for bundle $TEST_PROJECT_NAME: 40.00%")
checkOutputContains("Rule violated for bundle $TEST_PROJECT_NAME: instructions covered ratio is 0.4, but expected minimum is 0.5")
}
}
@Test
fun `when coverage above default minimum, verify task outcome should be SUCCESS`() {
// Setup with 66% coverage, default is 50%
"One".also { className ->
sourceMainDir.createSourceJavaFile(className = className)
sourceAndroidTestDir.createAndroidTestKotlinFile(testFor = className)
}
"Two".also { className ->
sourceMainDir.createSourceKotlinFile(className = className)
sourceAndroidTestDir.createAndroidTestKotlinFile(testFor = className)
}
sourceMainDir.createSourceJavaFile(className = "Three")
runBuild("androidTestCoverageDebug") {
assertEquals(TaskOutcome.SUCCESS, task(":coverageVerificationDebug")?.outcome)
checkOutputContains("Code coverage for bundle $TEST_PROJECT_NAME: 66.67%")
}
}
@Test
fun `when coverage below configured minimum, verify task outcome should be FAILED`() {
// Setup with 66% coverage, configured to 80%
buildFile {
"""
codeCoverage {
minimumCoverage 0.8
failOnBelowMinimumCoverage true
}
"""
}
"One".also { className ->
sourceMainDir.createSourceKotlinFile(className = className)
sourceAndroidTestDir.createAndroidTestKotlinFile(testFor = className)
}
"Two".also { className ->
sourceMainDir.createSourceJavaFile(className = className)
sourceAndroidTestDir.createAndroidTestKotlinFile(testFor = className)
}
sourceMainDir.createSourceJavaFile(className = "Three")
runBuildAndFail("androidTestCoverageDebug") {
assertEquals(TaskOutcome.FAILED, task(":coverageVerificationDebug")?.outcome)
checkOutputContains("Code coverage for bundle $TEST_PROJECT_NAME: 66.67%")
}
}
@Test
fun `when coverage below configured minimum but allowed, verify task outcome should be SUCCESS with warning`() {
// Setup with 66% coverage, configured to 80%, with only warning
buildFile {
"""
codeCoverage {
minimumCoverage 0.8
failOnBelowMinimumCoverage false
}
"""
}
"One".also { className ->
sourceMainDir.createSourceKotlinFile(className = className)
sourceAndroidTestDir.createAndroidTestKotlinFile(testFor = className)
}
"Two".also { className ->
sourceMainDir.createSourceJavaFile(className = className)
sourceAndroidTestDir.createAndroidTestKotlinFile(testFor = className)
}
sourceMainDir.createSourceJavaFile(className = "Three")
runBuild("androidTestCoverageDebug") {
assertEquals(TaskOutcome.SUCCESS, task(":coverageVerificationDebug")?.outcome)
checkOutputContains("Code coverage for bundle $TEST_PROJECT_NAME: 66.67%")
checkOutputContains("Rule violated for bundle $TEST_PROJECT_NAME: instructions covered ratio is 0.6")
}
}
@Test
fun `when coverage above configured minimum, verify task outcome should be SUCCESS`() {
// Setup with 80% coverage, configured to 75%
buildFile {
"""
codeCoverage {
minimumCoverage 0.75
}
"""
}
"One".also { className ->
sourceMainDir.createSourceKotlinFile(className = className)
sourceAndroidTestDir.createAndroidTestKotlinFile(testFor = className)
}
"Two".also { className ->
sourceMainDir.createSourceJavaFile(className = className)
sourceAndroidTestDir.createAndroidTestKotlinFile(testFor = className)
}
"Three".also { className ->
sourceMainDir.createSourceJavaFile(className = className)
sourceAndroidTestDir.createAndroidTestKotlinFile(testFor = className)
}
"Four".also { className ->
sourceMainDir.createSourceKotlinFile(className = className)
sourceAndroidTestDir.createAndroidTestKotlinFile(testFor = className)
}
sourceMainDir.createSourceJavaFile(className = "Five")
runBuild("androidTestCoverageDebug") {
assertEquals(TaskOutcome.SUCCESS, task(":coverageVerificationDebug")?.outcome)
checkOutputContains("Code coverage for bundle $TEST_PROJECT_NAME: 80.00%")
}
}
@Test
fun `when coverage has configured excludes, verify task should ignore classes matching excludes`() {
// Setup with 50% coverage when excludes are disregarded, configured high but just with warning
buildFile {
"""
codeCoverage {
testExcludes = [
'**/Two*',
'**/Three*',
'**/Four*'
]
minimumCoverage 0.9
failOnBelowMinimumCoverage false
}
"""
}
"One".also { className ->
sourceMainDir.createSourceKotlinFile(className = className)
sourceAndroidTestDir.createAndroidTestKotlinFile(testFor = className)
}
sourceMainDir.createSourceKotlinFile(className = "Two")
sourceMainDir.createSourceJavaFile(className = "Three")
sourceMainDir.createSourceKotlinFile(className = "Four")
sourceMainDir.createSourceJavaFile(className = "Five")
runBuild("androidTestCoverageDebug") {
assertEquals(TaskOutcome.SUCCESS, task(":coverageVerificationDebug")?.outcome)
checkOutputContains("Code coverage for bundle $TEST_PROJECT_NAME: 50.00%")
checkOutputContains("Rule violated for bundle $TEST_PROJECT_NAME: instructions covered ratio is 0.5")
}
}
}
Abstract BaseTest claas
package com.oujji.sdk.android.gradle.quality.test
import org.gradle.testkit.runner.BuildResult
import org.gradle.testkit.runner.GradleRunner
import org.junit.Rule
import org.junit.rules.TemporaryFolder
abstract class BaseTest {
@Rule
@JvmField
val testProjectDir = TemporaryFolder()
private fun createBuild(arguments: List<String> = listOf()): GradleRunner = GradleRunner.create()
.withPluginClasspath()
.withProjectDir(testProjectDir.root)
.withArguments(arguments)
fun runBuild(vararg arguments: String, withResult: BuildResult.() -> (Unit) = {}): BuildResult =
createBuild(arguments.asList()).build().apply { withResult() }
fun runBuildAndFail(vararg arguments: String, withResult: BuildResult.() -> (Unit) = {}): BuildResult =
createBuild(arguments.asList()).buildAndFail().apply { withResult() }
companion object {
const val TEST_PROJECT_NAME = "QualityToolsTestProject"
}
}
GradleFiles.kt
package com.oujji.sdk.android.gradle.quality.test
import org.junit.rules.TemporaryFolder
import java.net.URL
internal fun TemporaryFolder.createGradleProperties() {
createProjectFile("gradle.properties") {
val proxyProps = System.getenv("https_proxy") ?: System.getenv("http_proxy")
?.let { if(it.startsWith("http")) it else "http://$it" }
?.let { URL(it) }
?.let {
"\nsystemProp.http.proxyHost=${it.host}\nsystemProp.http.proxyPort=${it.port}"
} ?: ""
"android.useAndroidX=true\n$proxyProps"
}
}
SetingsFile.kt
package com.oujji.sdk.android.gradle.quality.test
import org.junit.rules.TemporaryFolder
internal fun TemporaryFolder.createSettingsFile(projectName: String) = createProjectFile("settings.gradle") {
"""
rootProject.name = '${projectName}'
"""
}
ProjectFile.kt
package com.oujji.sdk.android.gradle.quality.test
import org.junit.rules.TemporaryFolder
import java.io.File
operator fun File.invoke(append: () -> String) = appendText(append().trimIndent())
fun File.createProjectFile(fileName: String, contents: () -> String) =
File(this, fileName).also { sourceFile ->
sourceFile.createNewFile()
sourceFile.writeText(contents().trimIndent())
}
fun TemporaryFolder.createProjectFile(fileName: String, contents: () -> String) =
newFile(fileName).apply { writeText(contents().trimIndent()) }
Environement :
------------------------------------------------------------
Gradle 7.1.1
------------------------------------------------------------
Build time: 2021-07-02 12:16:43 UTC
Revision: 774525a055494e0ece39f522ac7ad17498ce032c
Kotlin: 1.4.31
Groovy: 3.0.7
Ant: Apache Ant(TM) version 1.10.9 compiled on September 27 2020
JVM: 11.0.12 (Oracle Corporation 11.0.12+8-LTS-237)
OS: Mac OS X 11.5 x86_64
Solution 1:[1]
I had a problem that was giving me the same error message as Kumar states. I can't decern whether my problem and Kumar problem are the same, but I too was trying to manipulate a gradle build. In Kumar's cast, he seems to be trying to generate a build.gradle. In my case, I'm only trying to manipulate the "android" block of a build.gradle. So, there's at least some reason to believe that my solution might be related.
I did two things to solve the problem, but I'm not sure if both are necessary. First, I upgraded gradle from version 7.4 to 7.4.2. The release notes for 7.4.2 indicated numerous fixes and of course recommended developers to upgrade to this version. After doing that, I tried to do the build again. As a result of doing this, the error I was getting shifted. Instead of complaining about the bad cast, it now indicated that I had a problem in my AndroidManifest.xml file. My manifest file had the wrong class name in an application activity section of the manifest. I corrected the name, and the build worked fine.
For the record, I will state that I actual got displayed a different error message in my build, but by debugging my gradle build, I was able to catch the initial exception, and that exception was exactly the same as Kumar exception and that's why I say I got same error message.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Tom Rutchik |