Kotlin-dsl-samples: [Android] - "android.testOptions" Groovy closure issue

Created on 22 Jul 2017  路  25Comments  路  Source: gradle/kotlin-dsl-samples

How can I convert this to Kotlin DSL?

Groovy DSL(works):

android {
  testOptions {
    animationsDisabled = true
    unitTests {
      returnDefaultValues = true
      all {
        jacoco {
          includeNoLocationClasses = true
        }
      }
    }
  }
}

Source: https://github.com/jaredsburrows/android-gif-example/blob/master/build.gradle#L134

Kotlin DSL(does not work):

android {
  testOptions {
    animationsDisabled = true
    unitTests(delegateClosureOf<Any?> {
      unitTests.setReturnDefaultValues(true)
      // unitTests.all(closureOf<Test> {
      // })
    })
}

Source: https://github.com/jaredsburrows/android-gif-example/blob/build.gradle.kts/build.gradle.kts#L138

Most helpful comment

With the following extension method:

// This is a workaround for https://issuetracker.google.com/issues/78547461
fun com.android.build.gradle.internal.dsl.TestOptions.UnitTestOptions.all(block: Test.() -> Unit) =
        all(KotlinClosure1<Any, Test>({ (this as Test).apply(block) }, owner = this))

the Kotlin DSL could look clean, similar to Groovy:

  testOptions {
    animationsDisabled = true
    unitTests.apply {
      isReturnDefaultValues = true
      isIncludeAndroidResources = true
      all {
          extensions
            .getByType(JacocoTaskExtension::class.java)
            .isIncludeNoLocationClasses = true
      }
    }
    setExecution("ANDROID_TEST_ORCHESTRATOR")
  }

All 25 comments

The setReturnDefaultValues shows up as an error in the IDE but works via command line.

To solve the isIncludeNoLocationClasses, possibly?

configure<JacocoTaskExtension> {
  isIncludeNoLocationClasses = true
}

Using returnDefaultValues = true instead of setReturnDefaultValues( true ) yields: Cannot access 'returnDefaultValues': it is private in 'UnitTestOptions'

There's probably something fishy with the JavaBean property on the Android Gradle Plugin side.

As for the other configuration, here's a quite rude version that should work with the Kotlin DSL:

import com.android.build.gradle.internal.dsl.TestOptions

testOptions {
    animationsDisabled = true
    unitTests(delegateClosureOf<TestOptions.UnitTestOptions>{
        setReturnDefaultValues(true)
        all(KotlinClosure1<Any, Test>({
            (this as Test).also { testTask ->
                testTask.extensions.getByType(JacocoTaskExtension::class.java).setIncludeNoLocationClasses(true)
            }
        }, this))
    })
}

Maybe @bamboo will have an idea to make this nicer.

Thanks for this. setReturnDefaultValues kept showing up as "red" in my IDE. I hope the Android Plugin can fix their issues on their end.

UnitTestOptions class is an inner private class. In the IDE, it also shows up as "red". Is the Android Gradle Plugin limiting us?

UnitTestOptions class is an inner private class. In the IDE, it also shows up as "red". Is the Android Gradle Plugin limiting us?

Nope, this is #434

@eskatos Thanks again!

@eskatos is the an equivalent?

  testOptions {
    animationsDisabled = true
    unitTests(delegateClosureOf<TestOptions.UnitTestOptions> {
      setReturnDefaultValues(true)
    })
  }
tasks.all {
  when (this) {
    is JacocoTaskExtension -> {
      isIncludeNoLocationClasses = true
    }
  }
}

This is an issue with the Android Gradle plugin. Would you mind reporting this issue on their issue tracker?

@bamboo Sure. What would I describe as the issue? Make UnitTestOptions public?

UnitTestOptions is already public and in more recent versions is even accessible via the unitTests property which should allow the following configuration pattern:

android {    
    testOptions {
        unitTests.apply {
            isReturnDefaultValues = true
        }
    }
}

The original issue is that the Groovy-only com.android.build.gradle.internal.dsl.TestOptions#unitTests(Closure) configuration method should be replaced by the DSL agnostic com.android.build.gradle.internal.dsl.TestOptions#unitTests(org.gradle.api.Action<UnitTestOptions>) which would then allow the following, simpler pattern:

android {    
    testOptions {
        unitTests { // No .apply here
            isReturnDefaultValues = true
        }
    }
}

@bamboo Awesome. So if more recent versions are available, there is no need for me to make the issue?

It looks like it is getting closer :)

Here is what I have now:

  testOptions {
    animationsDisabled = true
    unitTests(delegateClosureOf<TestOptions.UnitTestOptions> {
      isReturnDefaultValues = true
      isIncludeAndroidResources = true
      all(KotlinClosure1<Any, Test>({
        (this as Test).also { testTask ->
          testTask.extensions
            .getByType(JacocoTaskExtension::class.java)
            .isIncludeNoLocationClasses = true
        }
      }, this))
    })
    setExecution("ANDROID_TEST_ORCHESTRATOR")
  }

Based on your last comment, I was able to remove delegateClosureOf<TestOptions.UnitTestOptions>:

  testOptions {
    animationsDisabled = true
    unitTests.apply {
      isReturnDefaultValues = true
      isIncludeAndroidResources = true
      all(KotlinClosure1<Any, Test>({
        (this as Test).also { testTask ->
          testTask.extensions
            .getByType(JacocoTaskExtension::class.java)
            .isIncludeNoLocationClasses = true
        }
      }, this))
    }
    setExecution("ANDROID_TEST_ORCHESTRATOR")
  }

@jaredsburrows, is there an AGP issue I can star?

@ming13 I am not sure, is there one @bamboo?

Just created an AGP issue for that kind of issues, let鈥檚 see how it goes.

With the following extension method:

// This is a workaround for https://issuetracker.google.com/issues/78547461
fun com.android.build.gradle.internal.dsl.TestOptions.UnitTestOptions.all(block: Test.() -> Unit) =
        all(KotlinClosure1<Any, Test>({ (this as Test).apply(block) }, owner = this))

the Kotlin DSL could look clean, similar to Groovy:

  testOptions {
    animationsDisabled = true
    unitTests.apply {
      isReturnDefaultValues = true
      isIncludeAndroidResources = true
      all {
          extensions
            .getByType(JacocoTaskExtension::class.java)
            .isIncludeNoLocationClasses = true
      }
    }
    setExecution("ANDROID_TEST_ORCHESTRATOR")
  }

@valeriyo can we somehow put this extension to buildSrc folder?

Should be totally possible, if you figure out how to add dependency for com.android.build.gradle.internal.dsl.TestOptions to be resolved. I tried adding

dependencies {
  implementation("com.android.tools.build:gradle:3.4.1")
}

and it didn't work.

I'm trying to set jvmArgs for my unit tests and nothing I tried seems to work.

Groovy, works:

testOptions {
        unitTests.all {
            jvmArgs '-noverify'
        }
    }

Kotlin, doesn't work:

testOptions {
        unitTests.all(KotlinClosure1<Any, Test>({
            (this as Test).also { jvmArgs("-noverify") }
        }, this))
    }

This too:

testOptions {
        unitTests.all(KotlinClosure1<Any, Test>({
            (this as Test).also { jvmArgs = listOf("-noverify") }
        }, this))
    }

Nothing seems to work, what am I missing?

@MahmoudAlyuDeen did you find any solution for this?

@PrasannaKumarChalla
Unfortunately, no.
For now, I edited Android Studio template for running tests to always include the -noverify flag.

FWIW.
This works

tasks.withType<Test>().all {
    jvmArgs("-noverify")
}

verified using gradle-5.1.1-all

I'm trying to set jvmArgs for my unit tests and nothing I tried seems to work.
[...]

testOptions {
        unitTests.all(KotlinClosure1<Any, Test>({
            (this as Test).also { jvmArgs("-noverify") }
        }, this))
    }

[...]
Nothing seems to work, what am I missing?

The closure owner must also be unitTests. But in the above example, this is refering to testOptions.
So it should look like this:

testOptions {
    unitTests.all(KotlinClosure1<Any, Test>({
        (this as Test).also { jvmArgs("-noverify") }
    }, unitTests))
}

With the following extension method:

// This is a workaround for https://issuetracker.google.com/issues/78547461
fun com.android.build.gradle.internal.dsl.TestOptions.UnitTestOptions.all(block: Test.() -> Unit) =
        all(KotlinClosure1<Any, Test>({ (this as Test).apply(block) }, owner = this))

the Kotlin DSL could look clean, similar to Groovy:

  testOptions {
    animationsDisabled = true
    unitTests.apply {
      isReturnDefaultValues = true
      isIncludeAndroidResources = true
      all {
          extensions
            .getByType(JacocoTaskExtension::class.java)
            .isIncludeNoLocationClasses = true
      }
    }
    setExecution("ANDROID_TEST_ORCHESTRATOR")
  }

The above code throws this error: Extension of type 'JacocoTaskExtension' does not exist. Currently registered extension types: [ExtraPropertiesExtension]

Any workaround for this? I'm using jacoco 0.8.5

~SOLVED~ (unrelated):

I'm using condition inside plugins block, thus the unitTest.all needs to be addressed as well:

plugins {
   if (System.property(CI_ENV).toBoolean == true) id(jacoco)
}

android {
  //....
   testOptions {
     unitTest.apply {
       all {
            if (System.property(CI_ENV).toBoolean == true) 
                extensions
                   .getByType(JacocoTaskExtension::class.java)
                   .isIncludeNoLocationClasses = true

             jvmArgs = mutableListOf("-XX:MaxPermSize=256m", "-ea", "-noverify") // if needed for robolectric
        }
     }
   }
   // ....
}

Unfortunately, my above statements was never a workaround, it's just to skip the isIncludeNoLocationClasses but when I'm running on CI_ENV it's still doesn't works

Any suggestions?

Looks like fixed in: AGP 4.1 https://issuetracker.google.com/issues/78547461#comment7

Was this page helpful?
0 / 5 - 0 ratings

Related issues

cbeams picture cbeams  路  3Comments

jmfayard picture jmfayard  路  3Comments

jaredsburrows picture jaredsburrows  路  3Comments

iNikem picture iNikem  路  3Comments

eskatos picture eskatos  路  3Comments