From b3a1714d63b27fbfcc94c8733f1ebe98d19ae3ac Mon Sep 17 00:00:00 2001 From: Marcel Schnelle Date: Sun, 3 Dec 2017 19:41:10 +0900 Subject: [PATCH 01/12] Completed migration to new DSL location for junitPlatform & dependency handlers Dependency Handlers: * Replaced junit5() with junit5.unitTests() * Replaced junit5Params() with junit5.parameterized() * Replaced junit5EmbeddedRuntime() with junit5.unitTestsRuntime() * Added junit5.instrumentationTests() (Backwards-compatible versions of the old handlers are still available, however they log a warning when used.) Moved junitPlatform DSL from to android.testOptions (Backwards-compatible usage of the old DSL location is still allowed, however that logs a warning when used.) --- .../plugins/junit5/AGP2PluginSpec.groovy | 26 ++- .../plugins/junit5/AGP3PluginSpec.groovy | 31 ++-- .../plugins/junit5/BasePluginSpec.groovy | 151 +++++++++++++++--- .../plugins/junit5/CallableGroovyTests.groovy | 4 +- .../plugins/junit5/CallableKotlinTests.kt | 4 +- android-junit5/build.gradle | 11 +- .../AndroidJUnitPlatformExtension.groovy | 43 ++++- .../plugins/junit5/ExtensionProxy.groovy | 41 +++++ .../gradle/plugins/junit5/LogUtils.groovy | 14 ++ .../gradle/plugins/junit5/Constants.kt | 5 +- .../gradle/plugins/junit5/Dependencies.kt | 138 +++++++++++++++- .../gradle/plugins/junit5/Extensions.kt | 48 +++--- .../gradle/plugins/junit5/Plugin.kt | 72 ++++----- .../integrations/InstrumentationTests.kt | 38 ----- .../gradle/plugins/junit5/tasks/Base.kt | 2 - .../gradle/plugins/junit5/tasks/Jacoco.kt | 3 +- .../gradle/plugins/junit5/versions.properties | 11 +- gradle.properties | 6 + 18 files changed, 469 insertions(+), 179 deletions(-) create mode 100644 android-junit5/src/main/groovy/de/mannodermaus/gradle/plugins/junit5/ExtensionProxy.groovy create mode 100644 android-junit5/src/main/groovy/de/mannodermaus/gradle/plugins/junit5/LogUtils.groovy delete mode 100644 android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/integrations/InstrumentationTests.kt diff --git a/android-junit5-tests/testAgp2x/src/test/groovy/de/mannodermaus/gradle/plugins/junit5/AGP2PluginSpec.groovy b/android-junit5-tests/testAgp2x/src/test/groovy/de/mannodermaus/gradle/plugins/junit5/AGP2PluginSpec.groovy index 6e39fd74..0ece4e39 100644 --- a/android-junit5-tests/testAgp2x/src/test/groovy/de/mannodermaus/gradle/plugins/junit5/AGP2PluginSpec.groovy +++ b/android-junit5-tests/testAgp2x/src/test/groovy/de/mannodermaus/gradle/plugins/junit5/AGP2PluginSpec.groovy @@ -4,6 +4,7 @@ import de.mannodermaus.gradle.plugins.junit5.tasks.AndroidJUnit5JacocoReport import de.mannodermaus.gradle.plugins.junit5.tasks.AndroidJUnit5UnitTest import de.mannodermaus.gradle.plugins.junit5.util.TaskUtils import org.gradle.api.Project +import org.gradle.api.ProjectConfigurationException /* * Unit testing the integration of JUnit 5 @@ -78,7 +79,7 @@ class AGP2PluginSpec extends BasePluginSpec { expectedVariantTasks.each { assert runAllTask.getDependsOn().contains(it) } } - def "Instrumentation Test Integration: Works with Product Flavors"() { + def "Instrumentation Test Integration: Attempting to use library without enabling throws Exception"() { when: Project project = factory.newProject(rootProject()) .asAndroidApplication() @@ -86,25 +87,20 @@ class AGP2PluginSpec extends BasePluginSpec { .build() project.android { - productFlavors { - paid { - junit5InstrumentedTestsEnabled false - } - free { - junit5InstrumentedTestsEnabled true - } + testOptions.junitPlatform.instrumentationTests { + enabled = false } } + project.dependencies { + androidTestCompile junit5.instrumentationTests() + } + project.evaluate() then: - def enabledFlavor = project.android.productFlavors.getByName("free") - def enabledArgs = enabledFlavor.getTestInstrumentationRunnerArguments() - assert enabledArgs.containsKey("runnerBuilder") - - def disabledFlavor = project.android.productFlavors.getByName("paid") - def disabledArgs = disabledFlavor.getTestInstrumentationRunnerArguments() - assert !disabledArgs.containsKey("runnerBuilder") + def expect = thrown(ProjectConfigurationException) + expect.message.contains("The JUnit 5 Instrumentation Test library can only be used " + + "if support for them is explicitly enabled as well.") } } diff --git a/android-junit5-tests/testAgp3x/src/test/groovy/de/mannodermaus/gradle/plugins/junit5/AGP3PluginSpec.groovy b/android-junit5-tests/testAgp3x/src/test/groovy/de/mannodermaus/gradle/plugins/junit5/AGP3PluginSpec.groovy index 524626f9..04461aed 100644 --- a/android-junit5-tests/testAgp3x/src/test/groovy/de/mannodermaus/gradle/plugins/junit5/AGP3PluginSpec.groovy +++ b/android-junit5-tests/testAgp3x/src/test/groovy/de/mannodermaus/gradle/plugins/junit5/AGP3PluginSpec.groovy @@ -4,6 +4,7 @@ import de.mannodermaus.gradle.plugins.junit5.tasks.AndroidJUnit5JacocoReport import de.mannodermaus.gradle.plugins.junit5.tasks.AndroidJUnit5UnitTest import de.mannodermaus.gradle.plugins.junit5.util.TaskUtils import org.gradle.api.Project +import org.gradle.api.ProjectConfigurationException /* * Unit testing the integration of JUnit 5 @@ -144,7 +145,7 @@ class AGP3PluginSpec extends BasePluginSpec { project.tasks.findByName("jacocoTestReportRelease") == null } - def "Instrumentation Test Integration: Works with Product Flavors"() { + def "Instrumentation Test Integration: Attempting to use library without enabling throws Exception"() { when: Project project = factory.newProject(rootProject()) .asAndroidApplication() @@ -152,30 +153,20 @@ class AGP3PluginSpec extends BasePluginSpec { .build() project.android { - // "All flavors must now belong to a named flavor dimension" - flavorDimensions "price" - - productFlavors { - paid { - dimension "price" - junit5InstrumentedTestsEnabled false - } - free { - dimension "price" - junit5InstrumentedTestsEnabled true - } + testOptions.junitPlatform.instrumentationTests { + enabled = false } } + project.dependencies { + androidTestImplementation junit5.instrumentationTests() + } + project.evaluate() then: - def enabledFlavor = project.android.productFlavors.getByName("free") - def enabledArgs = enabledFlavor.getTestInstrumentationRunnerArguments() - assert enabledArgs.containsKey("runnerBuilder") - - def disabledFlavor = project.android.productFlavors.getByName("paid") - def disabledArgs = disabledFlavor.getTestInstrumentationRunnerArguments() - assert !disabledArgs.containsKey("runnerBuilder") + def expect = thrown(ProjectConfigurationException) + expect.message.contains("The JUnit 5 Instrumentation Test library can only be used " + + "if support for them is explicitly enabled as well.") } } diff --git a/android-junit5-tests/testCommon/src/test/groovy/de/mannodermaus/gradle/plugins/junit5/BasePluginSpec.groovy b/android-junit5-tests/testCommon/src/test/groovy/de/mannodermaus/gradle/plugins/junit5/BasePluginSpec.groovy index 6da5c3ee..62ebb708 100644 --- a/android-junit5-tests/testCommon/src/test/groovy/de/mannodermaus/gradle/plugins/junit5/BasePluginSpec.groovy +++ b/android-junit5-tests/testCommon/src/test/groovy/de/mannodermaus/gradle/plugins/junit5/BasePluginSpec.groovy @@ -82,14 +82,14 @@ abstract class BasePluginSpec extends Specification { project.file("build.gradle").withWriter { it.write(""" - buildscript { - dependencies { - classpath files($environment.pluginClasspathString) - } - } - - apply plugin: "de.mannodermaus.android-junit5" - """) + buildscript { + dependencies { + classpath files($environment.pluginClasspathString) + } + } + + apply plugin: "de.mannodermaus.android-junit5" + """) } def result = GradleRunner.create() @@ -108,13 +108,20 @@ abstract class BasePluginSpec extends Specification { .buildAndEvaluate() then: - def junit5 = project.dependencies.junit5 - def junit5Params = project.dependencies.junit5Params - def junit5EmbeddedRuntime = project.dependencies.junit5EmbeddedRuntime + assert project.dependencies.junit5 != null + } + + // FIXME When the deprecation is removed in a future major update, delete this test as well + def "Deprecated Dependency Handlers still work"() { + when: + Project project = factory.newProject(rootProject()) + .asAndroidApplication() + .buildAndEvaluate() - assert junit5 != null - assert junit5Params != null - assert junit5EmbeddedRuntime != null + then: + assert project.dependencies.junit5.unitTests() == project.dependencies.junit5() + assert project.dependencies.junit5.parameterized() == project.dependencies.junit5Params() + assert project.dependencies.junit5.unitTestsRuntime() == project.dependencies.junit5EmbeddedRuntime() } def "Overwrite Dependency Versions"() { @@ -123,22 +130,69 @@ abstract class BasePluginSpec extends Specification { .asAndroidApplication() .build() + project.android { + testOptions.junitPlatform { + platformVersion = "1.3.3.7" + jupiterVersion = "0.8.15" + vintageVersion = "1.2.3" + + instrumentationTests { + enabled = true + version = "4.8.15" + } + } + } + + project.evaluate() + + then: + def ju5Deps = project.dependencies.junit5.unitTests() as List + assert ju5Deps.find { it.group == "org.junit.platform" && it.version == "1.3.3.7" } != null + assert ju5Deps.find { it.group == "org.junit.jupiter" && it.version == "0.8.15" } != null + assert ju5Deps.find { it.group == "org.junit.vintage" && it.version == "1.2.3" } != null + + def ju5ParamsDeps = project.dependencies.junit5.parameterized() as List + assert ju5ParamsDeps.find { it.group == "org.junit.jupiter" && it.version == "0.8.15" } != null + + def ju5InstrumentationDeps = project.dependencies.junit5.instrumentationTests() as List + assert ju5InstrumentationDeps.find { + it.group == "de.mannodermaus.junit5" && it.version == "4.8.15" + } != null + } + + // FIXME When the deprecation is removed in a future major update, delete this test as well + def "Using the old DSL to configure JUnit 5 properly delegates"() { + when: + Project project = factory.newProject(rootProject()) + .asAndroidApplication() + .build() + project.junitPlatform { platformVersion = "1.3.3.7" jupiterVersion = "0.8.15" vintageVersion = "1.2.3" + + instrumentationTests { + enabled = true + version = "4.8.15" + } } project.evaluate() then: - def ju5Deps = project.dependencies.junit5() as List + def ju5Deps = project.dependencies.junit5.unitTests() as List assert ju5Deps.find { it.group == "org.junit.platform" && it.version == "1.3.3.7" } != null assert ju5Deps.find { it.group == "org.junit.jupiter" && it.version == "0.8.15" } != null assert ju5Deps.find { it.group == "org.junit.vintage" && it.version == "1.2.3" } != null - def ju5ParamsDeps = project.dependencies.junit5Params() as List + def ju5ParamsDeps = project.dependencies.junit5.parameterized() as List assert ju5ParamsDeps.find { it.group == "org.junit.jupiter" && it.version == "0.8.15" } != null + + def ju5InstrumentationDeps = project.dependencies.junit5.instrumentationTests() as List + assert ju5InstrumentationDeps.find { + it.group == "de.mannodermaus.junit5" && it.version == "4.8.15" + } != null } def "android.testOptions: jvmArgs are properly applied"() { @@ -235,8 +289,10 @@ abstract class BasePluginSpec extends Specification { } } - project.junitPlatform { - applyDefaultTestOptions = false + project.android { + testOptions.junitPlatform { + applyDefaultTestOptions false + } } project.evaluate() @@ -260,8 +316,10 @@ abstract class BasePluginSpec extends Specification { .applyJunit5Plugin() .build() - project.junitPlatform { - reportsDir project.file("${project.buildDir.absolutePath}/other-path/test-reports") + project.android { + testOptions.junitPlatform { + reportsDir project.file("${project.buildDir.absolutePath}/other-path/test-reports") + } } project.evaluate() @@ -441,7 +499,48 @@ abstract class BasePluginSpec extends Specification { project.tasks.findByName("jacocoTestReportRelease") == null } - def "Instrumentation Test Integration: Enabled"() { + def "Instrumentation Test Integration: Doesn't attach RunnerBuilder if disabled"() { + when: + Project project = factory.newProject(rootProject()) + .asAndroidApplication() + .applyJunit5Plugin() + .build() + + project.android { + testOptions.junitPlatform.instrumentationTests { + enabled = false + } + } + + project.evaluate() + + then: + def args = project.android.defaultConfig.getTestInstrumentationRunnerArguments() + assert !args.containsKey("runnerBuilder") + } + + def "Instrumentation Test Integration: Attaches RunnerBuilder"() { + when: + Project project = factory.newProject(rootProject()) + .asAndroidApplication() + .applyJunit5Plugin() + .build() + + project.android { + testOptions.junitPlatform.instrumentationTests { + enabled = true + } + } + + project.evaluate() + + then: + def args = project.android.defaultConfig.getTestInstrumentationRunnerArguments() + assert args.containsKey("runnerBuilder") + assert args["runnerBuilder"].contains("AndroidJUnit5Builder") + } + + def "Instrumentation Test Integration: Appends RunnerBuilder if another is already present"() { when: Project project = factory.newProject(rootProject()) .asAndroidApplication() @@ -450,7 +549,11 @@ abstract class BasePluginSpec extends Specification { project.android { defaultConfig { - junit5InstrumentedTestsEnabled true + testInstrumentationRunnerArgument "runnerBuilder", "com.something.else.OtherRunnerBuilder" + } + + testOptions.junitPlatform.instrumentationTests { + enabled = true } } @@ -459,5 +562,9 @@ abstract class BasePluginSpec extends Specification { then: def args = project.android.defaultConfig.getTestInstrumentationRunnerArguments() assert args.containsKey("runnerBuilder") + + // Intentional comma + assert args["runnerBuilder"].contains("com.something.else.OtherRunnerBuilder,") + assert args["runnerBuilder"].contains("AndroidJUnit5Builder") } } diff --git a/android-junit5-tests/testCommon/src/test/groovy/de/mannodermaus/gradle/plugins/junit5/CallableGroovyTests.groovy b/android-junit5-tests/testCommon/src/test/groovy/de/mannodermaus/gradle/plugins/junit5/CallableGroovyTests.groovy index 8a07f65e..a300a412 100644 --- a/android-junit5-tests/testCommon/src/test/groovy/de/mannodermaus/gradle/plugins/junit5/CallableGroovyTests.groovy +++ b/android-junit5-tests/testCommon/src/test/groovy/de/mannodermaus/gradle/plugins/junit5/CallableGroovyTests.groovy @@ -4,8 +4,8 @@ import org.junit.Test class CallableGroovyTests { @Test - void callable() { - def obj = new Callable({ 2 + 2 }) + void callable0() { + def obj = new Callable0({ 2 + 2 }) assert obj() == 4 } diff --git a/android-junit5-tests/testCommon/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/CallableKotlinTests.kt b/android-junit5-tests/testCommon/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/CallableKotlinTests.kt index 9377f3e3..dc29ad9a 100644 --- a/android-junit5-tests/testCommon/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/CallableKotlinTests.kt +++ b/android-junit5-tests/testCommon/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/CallableKotlinTests.kt @@ -4,8 +4,8 @@ import org.junit.Test class CallableKotlinTests { @Test - fun callable() { - val obj = Callable { 2 + 2 } + fun callable0() { + val obj = Callable0 { 2 + 2 } assert(obj() == 4) } diff --git a/android-junit5/build.gradle b/android-junit5/build.gradle index e13ab148..5d8117de 100644 --- a/android-junit5/build.gradle +++ b/android-junit5/build.gradle @@ -48,11 +48,12 @@ gradlePlugin { import org.apache.tools.ant.filters.ReplaceTokens processResources { - def tokens = [ANDROID_JUNIT5_VERSION: PLUGIN_RUNTIME_VERSION_NAME, - JUNIT4_VERSION : JUNIT4_VERSION, - JUNIT_PLATFORM_VERSION: JUNIT_PLATFORM_VERSION, - JUNIT_JUPITER_VERSION : JUNIT_JUPITER_VERSION, - JUNIT_VINTAGE_VERSION : JUNIT_VINTAGE_VERSION] + def tokens = [ANDROID_JUNIT5_VERSION : PLUGIN_RUNTIME_VERSION_NAME, + JUNIT4_VERSION : JUNIT4_VERSION, + INSTRUMENTATION_TEST_VERSION: INSTRUMENTATION_VERSION_NAME, + JUNIT_PLATFORM_VERSION : JUNIT_PLATFORM_VERSION, + JUNIT_JUPITER_VERSION : JUNIT_JUPITER_VERSION, + JUNIT_VINTAGE_VERSION : JUNIT_VINTAGE_VERSION] inputs.properties(tokens) diff --git a/android-junit5/src/main/groovy/de/mannodermaus/gradle/plugins/junit5/AndroidJUnitPlatformExtension.groovy b/android-junit5/src/main/groovy/de/mannodermaus/gradle/plugins/junit5/AndroidJUnitPlatformExtension.groovy index 7519c6be..8cda07a4 100644 --- a/android-junit5/src/main/groovy/de/mannodermaus/gradle/plugins/junit5/AndroidJUnitPlatformExtension.groovy +++ b/android-junit5/src/main/groovy/de/mannodermaus/gradle/plugins/junit5/AndroidJUnitPlatformExtension.groovy @@ -1,6 +1,8 @@ package de.mannodermaus.gradle.plugins.junit5 +import com.android.annotations.NonNull import org.gradle.api.Project +import org.gradle.util.ConfigureUtil import org.junit.platform.gradle.plugin.JUnitPlatformExtension import javax.annotation.Nullable @@ -19,12 +21,10 @@ class AndroidJUnitPlatformExtension extends JUnitPlatformExtension { super(project) } - /** - * The version of JUnit Jupiter to use.*/ + /** The version of JUnit Jupiter to use.*/ @Nullable String jupiterVersion - /** - * The version of JUnit Vintage Engine to use. */ + /** The version of JUnit Vintage Engine to use. */ @Nullable String vintageVersion @@ -38,4 +38,39 @@ class AndroidJUnitPlatformExtension extends JUnitPlatformExtension { * - jvmArgs * - systemProperties */ boolean applyDefaultTestOptions = true + + /** + * Options for controlling instrumentation test execution with JUnit 5. + * + * @since 1.0.22 + */ + private final InstrumentationTestOptions instrumentationTests = new InstrumentationTestOptions() + + /** + * Configures instrumentation test options. + * + * @since 1.0.22 + */ + void instrumentationTests(Closure closure) { + ConfigureUtil.configure(closure, instrumentationTests) + } + + /** + * Configures instrumentation test options. + * + * @since 1.0.22 + */ + @NonNull + InstrumentationTestOptions getInstrumentationTests() { return instrumentationTests } + + /** + * Options for controlling instrumentation test execution.*/ + static class InstrumentationTestOptions { + + /** Whether or not to enable support for JUnit 5 instrumentation tests. */ + boolean enabled = false + + /** The version of the instrumentation companion library to use. */ + @Nullable String version + } } diff --git a/android-junit5/src/main/groovy/de/mannodermaus/gradle/plugins/junit5/ExtensionProxy.groovy b/android-junit5/src/main/groovy/de/mannodermaus/gradle/plugins/junit5/ExtensionProxy.groovy new file mode 100644 index 00000000..ee8f4458 --- /dev/null +++ b/android-junit5/src/main/groovy/de/mannodermaus/gradle/plugins/junit5/ExtensionProxy.groovy @@ -0,0 +1,41 @@ +package de.mannodermaus.gradle.plugins.junit5 + +import org.gradle.api.Project + +/** + * Temporary Proxy class, used to promote the new way of specifying + * JUnit Platform config parameters through android.testOptions, + * while still allowing the "old way" to co-exist for a bit.*/ +class ExtensionProxy { + + static def warning = "You're using the old way of configuring JUnit 5 in your project. " + + "This is deprecated behavior and subject to removal in a future version of the plugin. " + + "Please move your 'junitPlatform' clause into 'android.testOptions'!" + + private final Project project + private final AndroidJUnitPlatformExtension delegate + + ExtensionProxy(Project project, AndroidJUnitPlatformExtension delegate) { + this.project = project + this.delegate = delegate + } + + Object methodMissing(String methodName, Object args) { + logWarning() + return delegate.invokeMethod(methodName, args) + } + + Object propertyMissing(String propertyName) { + logWarning() + return delegate.getProperty(propertyName) + } + + def propertyMissing(String propertyName, Object value) { + logWarning() + delegate.setProperty(propertyName, value) + } + + private def logWarning() { + LogUtils.warning(project, warning) + } +} diff --git a/android-junit5/src/main/groovy/de/mannodermaus/gradle/plugins/junit5/LogUtils.groovy b/android-junit5/src/main/groovy/de/mannodermaus/gradle/plugins/junit5/LogUtils.groovy new file mode 100644 index 00000000..954516c5 --- /dev/null +++ b/android-junit5/src/main/groovy/de/mannodermaus/gradle/plugins/junit5/LogUtils.groovy @@ -0,0 +1,14 @@ +package de.mannodermaus.gradle.plugins.junit5 + +import org.gradle.api.Project + +/** + * Note: This is in Groovy until no other code in this language + * is using it anymore, or no more groovy files remain. + */ +class LogUtils { + + static def warning(Project project, String message) { + project.logger.warn("AGPBI: {\"kind\":\"warning\",\"text\":\"$message\"}") + } +} diff --git a/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/Constants.kt b/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/Constants.kt index a55120a3..e8ff6e2f 100644 --- a/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/Constants.kt +++ b/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/Constants.kt @@ -18,15 +18,14 @@ const val JUNIT_PLATFORM_VERSION_PROP = "junitPlatformVersion" const val JUNIT_JUPITER_VERSION_PROP = "junitJupiterVersion" const val JUNIT_VINTAGE_VERSION_PROP = "junitVintageVersion" const val JUNIT4_VERSION_PROP = "junit4Version" +const val INSTRUMENTATION_TEST_VERSION_PROP = "instrumentationTestVersion" // Instrumentation Test integration const val RUNNER_BUILDER_ARG = "runnerBuilder" const val JUNIT5_RUNNER_BUILDER_CLASS_NAME = "de.mannodermaus.junit5.AndroidJUnit5Builder" // Dependency Handler Names -const val DEP_HANDLER_NAME_JUNIT5 = "junit5" -const val DEP_HANDLER_NAME_PARAMETERIZED = "junit5Params" -const val DEP_HANDLER_NAME_RUNTIME = "junit5EmbeddedRuntime" +const val DEP_HANDLER_NAME = "junit5" // Configuration Names const val PARAM_NAME_ENABLE_INSTRUMENTED_TESTS = "junit5InstrumentedTestsEnabled" diff --git a/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/Dependencies.kt b/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/Dependencies.kt index 34e169cf..1ab499f4 100644 --- a/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/Dependencies.kt +++ b/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/Dependencies.kt @@ -1,16 +1,139 @@ package de.mannodermaus.gradle.plugins.junit5 +import groovy.lang.Closure import org.gradle.api.Project +import org.gradle.api.ProjectConfigurationException import org.gradle.api.artifacts.Dependency import java.util.Properties /* * Model classes holding information about the transitive dependencies of the plugin, - * exposed to consumers through its custom dependency handlers. + * exposed to consumers through the custom dependency handler. */ +/* Extensions */ + +private fun Project.logDeprecationWarning(oldName: String, newName: String) { + LogUtils.warning(this, "The JUnit 5 dependency on '$oldName' " + + "is deprecated and will be removed in a future version. Please use '$newName' instead!") +} + +/* Types */ + /** - * Data holder, serving as a gateway to the actual dependencies via its properties. + * Public-facing handler object, injected into the default DependencyHandler, + * exposing the different available methods to consumers. + */ +@Suppress("MemberVisibilityCanPrivate") +class JUnit5DependencyHandler( + private val project: Project, + defaults: Properties) : Closure(null) /* FIXME Part of junit5 deprecation */ { + + private val versions: Versions by lazy { + Versions( + project = project, + extension = project.junit5, + defaults = defaults) + } + + /* Public */ + + /** + * Retrieves the list of dependencies related to + * running Unit Tests on the JUnit Platform with Android. + */ + fun unitTests() = listOf( + versions.others.junit4, + versions.jupiter.api, + versions.platform.engine, + versions.jupiter.engine, + versions.vintage.engine, + + // Only needed to run tests in an Android Studio that bundles an older version + // (see also http://junit.org/junit5/docs/current/user-guide/#running-tests-ide-intellij-idea) + versions.platform.launcher, + versions.platform.console + ) + + /** + * Retrieves the list of dependencies related to + * writing Parameterized Tests. + */ + fun parameterized() = listOf( + versions.jupiter.params + ) + + /** + * Retrieves the list of dependencies related to + * executing Unit Tests in Android Studio 3 properly. + */ + fun unitTestsRuntime() = listOf( + versions.others.embeddedRuntime + ) + + /** + * Retrieves the list of dependencies related to + * running Instrumentation Tests on the JUnit Platform with Android. + */ + fun instrumentationTests(): List { + // Abort if JUnit 5 Instrumentation Tests aren't enabled, + // since that would cause confusion otherwise. + if (!project.junit5.instrumentationTests.enabled) { + @Suppress("NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS") + throw ProjectConfigurationException( + "The JUnit 5 Instrumentation Test library can only be used " + + "if support for them is explicitly enabled as well.\n" + + "Please add the following to your build.gradle\n:" + + "android.defaultConfig.testOptions.instrumentationTests.enabled = true", null) + } + + return listOf(versions.others.instrumentationTest) + } + + /* Internal */ + + internal fun configure() { + // "dependencies.junit5" is the gateway to the sharded dependency groups + project.dependencies.ext[DEP_HANDLER_NAME] = this + + // FIXME Deprecation ---------------------------------------------------------------------------------------------------- + // "dependencies.junit5()" is the old way to specify unit tests + // this backwards compatibility is realized through the invoke() operator + // (see class declaration!) + + // "dependencies.junit5Params()" is the old way to specify parameterized tests + project.dependencies.ext["junit5Params"] = Callable0 { + project.logDeprecationWarning(oldName = "junit5Params()", newName = "junit5.parameterized()") + this.parameterized() + } + + // "dependencies.junit5EmbeddedRuntime()" is the old way to specify the embedded runtime + project.dependencies.ext["junit5EmbeddedRuntime"] = Callable0 { + project.logDeprecationWarning( + oldName = "junit5EmbeddedRuntime()", + newName = "junit5.unitTestsRuntime()") + this.unitTestsRuntime() + } + } + + // "dependencies.junit5()" is the old way to specify unit tests + @Suppress("MemberVisibilityCanPrivate") + operator fun invoke(): List { + project.logDeprecationWarning(oldName = "junit5()", newName = "junit5.unitTests()") + return this.unitTests() + } + + @Suppress("unused") + fun doCall(): List { + return this() + } + // END Deprecation ---------------------------------------------------------------------------------------------------- +} + +/* Internal API */ + +/** + * Internal data holder, serving as a gateway to the actual dependencies via its properties. */ class Versions( project: Project, @@ -20,7 +143,7 @@ class Versions( val jupiter = Jupiter(project, extension, defaults) val platform = Platform(project, extension, defaults) val vintage = Vintage(project, extension, defaults) - val others = Other(project, defaults) + val others = Other(project, extension, defaults) } abstract class BaseDependency(private val project: Project) { @@ -96,6 +219,7 @@ class Vintage( */ class Other( project: Project, + private val extension: AndroidJUnitPlatformExtension, properties: Properties ) : BaseDependency(project) { @@ -108,4 +232,12 @@ class Other( groupId = "junit", artifactId = "junit", version = properties.getProperty(JUNIT4_VERSION_PROP)) + + val instrumentationTest by lazy { + dependency( + groupId = "de.mannodermaus.junit5", + artifactId = "android-instrumentation-test", + version = extension.instrumentationTests.version ?: + properties.getProperty(INSTRUMENTATION_TEST_VERSION_PROP)) + } } diff --git a/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/Extensions.kt b/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/Extensions.kt index e4b24b64..27e4d441 100644 --- a/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/Extensions.kt +++ b/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/Extensions.kt @@ -4,7 +4,7 @@ import com.android.build.gradle.BaseExtension import com.android.build.gradle.api.BaseVariant import com.android.build.gradle.api.UnitTestVariant import com.android.build.gradle.internal.api.TestedVariant -import com.android.builder.model.ProductFlavor +import com.android.build.gradle.internal.dsl.TestOptions import de.mannodermaus.gradle.plugins.junit5.tasks.AndroidJUnit5JacocoReport import de.mannodermaus.gradle.plugins.junit5.tasks.AndroidJUnit5UnitTest import groovy.lang.Closure @@ -41,6 +41,23 @@ fun loadProperties(resource: String): Properties { return properties } +/** + * Adds the provided key-value pair to the Map. + * If there already is a value associated with the key, + * the value is appended to the end of the current value + * using the given delimiter. + */ +fun MutableMap.append( + key: String, value: String, delimiter: String = ","): String? { + val insertedValue = if (containsKey(key)) { + "${this[key]}$delimiter$value" + } else { + value + } + + return this.put(key, insertedValue) +} + /* * "Extension" Extension Functions: * Shorthand properties to access different plugins' extension models. @@ -61,10 +78,11 @@ val FiltersExtension.packages val FiltersExtension.engines get() = extensionByName(ENGINES_EXTENSION_NAME) +@Deprecated(message = "will be removed") val AndroidJUnitPlatformExtension.jacoco get() = extensionByName(JACOCO_EXTENSION_NAME) -val Project.junit5 +val TestOptions.junitPlatform get() = extensionByName(EXTENSION_NAME) val Project.jacoco @@ -78,10 +96,10 @@ val AndroidJUnit5UnitTest.jacoco /** * Create & add an Extension to the given container by name. */ -inline fun Any.createExtension( +inline fun Any.extend( name: String, args: Array = emptyArray(), - noinline init: (T.() -> Unit)? = null): T { + noinline init: ((T) -> Unit)? = null): T { // Access the Extension container of an object, // or raise an Exception if none are available if (this !is ExtensionAware) { @@ -89,7 +107,7 @@ inline fun Any.createExtension( } // Create & Configure the new extension - val created = this.extensions.create(name, T::class.java, *args) + val created: T = this.extensions.create(name, T::class.java, *args) init?.let { init(created) } return created } @@ -125,6 +143,9 @@ fun Project.hasPlugin(name: String) = this.plugins.findPlugin(name) != null val Project.android: BaseExtension get() = this.extensions.getByName("android") as BaseExtension +val Project.junit5: AndroidJUnitPlatformExtension + get() = this.android.testOptions.junitPlatform + /** * Access the extra properties of a DependencyHandler. * Equivalent to "DependencyHandler#ext" in Groovy. @@ -136,17 +157,6 @@ val DependencyHandler.ext: ExtraPropertiesExtension ExtraPropertiesExtension.EXTENSION_NAME) as ExtraPropertiesExtension } -/** - * Access the extra properties of a ProductFlavor. - * Equivalent to "ProductFlavor#ext" in Groovy. - */ -val ProductFlavor.ext: ExtraPropertiesExtension - get() { - val aware = this as ExtensionAware - return aware.extensions.getByName( - ExtraPropertiesExtension.EXTENSION_NAME) as ExtraPropertiesExtension - } - val BaseVariant.unitTestVariant: UnitTestVariant get() { if (this !is TestedVariant) { @@ -210,12 +220,12 @@ fun Project.withDependencies(defaults: Properties, config: (Versions) -> Any): A * */ @Suppress("unused") -class Callable(private val body: () -> Any) : Closure(null) { +class Callable0(private val body: () -> R) : Closure(null) { /** Kotlin's call syntax */ - operator fun invoke(): Any = body() + operator fun invoke(): R = body() /** Groovy's call syntax */ - fun doCall(): Any = body() + fun doCall(): R = body() } /** diff --git a/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/Plugin.kt b/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/Plugin.kt index ac6a8f50..1990d658 100644 --- a/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/Plugin.kt +++ b/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/Plugin.kt @@ -1,7 +1,6 @@ package de.mannodermaus.gradle.plugins.junit5 import com.android.build.gradle.api.BaseVariant -import de.mannodermaus.gradle.plugins.junit5.integrations.attachInstrumentationTestSupport import de.mannodermaus.gradle.plugins.junit5.providers.DirectoryProvider import de.mannodermaus.gradle.plugins.junit5.providers.JavaDirectoryProvider import de.mannodermaus.gradle.plugins.junit5.providers.KotlinDirectoryProvider @@ -35,22 +34,30 @@ class AndroidJUnitPlatformPlugin : Plugin { project.configureDependencies() project.afterEvaluate { it.configureTasks() + it.applyConfigurationParameters() } } private fun Project.configureExtensions() { - createExtension(EXTENSION_NAME, arrayOf(this)) { - createExtension(SELECTORS_EXTENSION_NAME) - createExtension(FILTERS_EXTENSION_NAME) { - createExtension(PACKAGES_EXTENSION_NAME) - createExtension(TAGS_EXTENSION_NAME) - createExtension(ENGINES_EXTENSION_NAME) - } - createExtension(JACOCO_EXTENSION_NAME) - } - - // Connect with integration libraries - attachInstrumentationTestSupport() + // Hook the JUnit Platform configuration into the Android testOptions + android.testOptions + .extend(EXTENSION_NAME, arrayOf(this)) { ju5 -> + ju5.extend(SELECTORS_EXTENSION_NAME) + ju5.extend(FILTERS_EXTENSION_NAME) { filters -> + filters.extend(PACKAGES_EXTENSION_NAME) + filters.extend(TAGS_EXTENSION_NAME) + filters.extend(ENGINES_EXTENSION_NAME) + } + + // TODO Deprecate & use actual Jacoco properties + ju5.extend(JACOCO_EXTENSION_NAME) + } + + // FIXME Deprecated -------------------------------------------------------------------------------- + // For backwards compatibility, still offer the "old" entry point "project.junitPlatform", + // which should redirect to the testOptions-based DSL dynamically + this.extend(EXTENSION_NAME, arrayOf(this, this.junit5)) + // END Deprecation -------------------------------------------------------------------------------- } private fun Project.configureDependencies() { @@ -71,31 +78,9 @@ class AndroidJUnitPlatformPlugin : Plugin { } } - // Create the dependency handlers for JUnit 5 - project.dependencies.ext[DEP_HANDLER_NAME_JUNIT5] = Callable { - withDependencies(defaults) { - listOf( - it.others.junit4, - it.jupiter.api, - it.platform.engine, - it.jupiter.engine, - it.vintage.engine, - - // Only needed to run tests in an Android Studio that bundles an older version - // (see also http://junit.org/junit5/docs/current/user-guide/#running-tests-ide-intellij-idea) - it.platform.launcher, - it.platform.console - ) - } - } - - project.dependencies.ext[DEP_HANDLER_NAME_PARAMETERIZED] = Callable { - withDependencies(defaults) { it.jupiter.params } - } - - project.dependencies.ext[DEP_HANDLER_NAME_RUNTIME] = Callable { - withDependencies(defaults) { it.others.embeddedRuntime } - } + // Create the custom dependency endpoints for JUnit 5 + val dependencyHandler = JUnit5DependencyHandler(this, defaults) + dependencyHandler.configure() } private fun Project.configureTasks() { @@ -131,4 +116,15 @@ class AndroidJUnitPlatformPlugin : Plugin { return providers } + + private fun Project.applyConfigurationParameters() { + // Consume Instrumentation Test options & + // apply configuration if enabled + if (junit5.instrumentationTests.enabled) { + // Attach the JUnit 5 RunnerBuilder automatically + // to the test instrumentation runner's parameters. + val runnerArgs = android.safeDefaultConfig.testInstrumentationRunnerArguments + runnerArgs.append(RUNNER_BUILDER_ARG, JUNIT5_RUNNER_BUILDER_CLASS_NAME) + } + } } diff --git a/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/integrations/InstrumentationTests.kt b/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/integrations/InstrumentationTests.kt deleted file mode 100644 index a25cbf8e..00000000 --- a/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/integrations/InstrumentationTests.kt +++ /dev/null @@ -1,38 +0,0 @@ -package de.mannodermaus.gradle.plugins.junit5.integrations - -import com.android.builder.model.ProductFlavor -import de.mannodermaus.gradle.plugins.junit5.Callable1 -import de.mannodermaus.gradle.plugins.junit5.JUNIT5_RUNNER_BUILDER_CLASS_NAME -import de.mannodermaus.gradle.plugins.junit5.PARAM_NAME_ENABLE_INSTRUMENTED_TESTS -import de.mannodermaus.gradle.plugins.junit5.RUNNER_BUILDER_ARG -import de.mannodermaus.gradle.plugins.junit5.android -import de.mannodermaus.gradle.plugins.junit5.ext -import de.mannodermaus.gradle.plugins.junit5.safeDefaultConfig -import org.gradle.api.Project - -/** - * Extends the default Android plugin models - * to allow for configuration of parameters - * related to the Instrumentation Test companion library. - */ -fun Project.attachInstrumentationTestSupport() { - // Attach extensions to both the default configuration - // as well as all product flavors, so that users can - // enable Instrumentation Test support for the scope they want - this.android.safeDefaultConfig.attachConfigureMethod() - - // Can't merge this with the default config b/c - // we have to use "all()" here to auto-configure - // any lazily appended flavor - this.android.productFlavors.all { it.attachConfigureMethod() } -} - -private fun ProductFlavor.attachConfigureMethod() { - this.ext[PARAM_NAME_ENABLE_INSTRUMENTED_TESTS] = Callable1 { enabled -> - if (enabled) { - this.testInstrumentationRunnerArguments[RUNNER_BUILDER_ARG] = JUNIT5_RUNNER_BUILDER_CLASS_NAME - } else { - this.testInstrumentationRunnerArguments.remove(RUNNER_BUILDER_ARG) - } - } -} diff --git a/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/tasks/Base.kt b/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/tasks/Base.kt index 360bffbc..80587e9b 100644 --- a/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/tasks/Base.kt +++ b/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/tasks/Base.kt @@ -3,7 +3,6 @@ package de.mannodermaus.gradle.plugins.junit5.tasks import com.android.build.gradle.api.BaseVariant import com.android.build.gradle.internal.scope.TaskConfigAction import com.android.build.gradle.internal.scope.VariantScope -import de.mannodermaus.gradle.plugins.junit5.junit5 import de.mannodermaus.gradle.plugins.junit5.variantData import org.gradle.api.Project import org.gradle.api.Task @@ -19,5 +18,4 @@ abstract class JUnit5TaskConfigAction( protected val variant: BaseVariant = testTask.variant protected val scope: VariantScope = variant.variantData.scope - protected val junit5 = project.junit5 } diff --git a/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/tasks/Jacoco.kt b/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/tasks/Jacoco.kt index 6cb51bbc..b3c4ddfa 100644 --- a/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/tasks/Jacoco.kt +++ b/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/tasks/Jacoco.kt @@ -1,6 +1,7 @@ package de.mannodermaus.gradle.plugins.junit5.tasks import de.mannodermaus.gradle.plugins.junit5.jacoco +import de.mannodermaus.gradle.plugins.junit5.junit5 import de.mannodermaus.gradle.plugins.junit5.junit5Info import de.mannodermaus.gradle.plugins.junit5.maybeCreate import de.mannodermaus.gradle.plugins.junit5.providers.DirectoryProvider @@ -68,7 +69,7 @@ open class AndroidJUnit5JacocoReport : JacocoReport() { reportTask.sourceDirectories = project.files(directoryProviders.mainSourceDirectories()) // Apply JUnit 5 configuration parameters - val junit5Jacoco = junit5.jacoco + val junit5Jacoco = project.junit5.jacoco reportTask.reports.apply { csv.isEnabled = junit5Jacoco.csvReport html.isEnabled = junit5Jacoco.htmlReport diff --git a/android-junit5/src/main/resources/de/mannodermaus/gradle/plugins/junit5/versions.properties b/android-junit5/src/main/resources/de/mannodermaus/gradle/plugins/junit5/versions.properties index c6043200..3df4a1d5 100644 --- a/android-junit5/src/main/resources/de/mannodermaus/gradle/plugins/junit5/versions.properties +++ b/android-junit5/src/main/resources/de/mannodermaus/gradle/plugins/junit5/versions.properties @@ -1,8 +1,9 @@ # suppress inspection "UnusedProperty" for whole file # (Populated from a Gradle "process resources" task) -androidJunit5Version = @ANDROID_JUNIT5_VERSION@ -junit4Version = @JUNIT4_VERSION@ +androidJunit5Version = @ANDROID_JUNIT5_VERSION@ +junit4Version = @JUNIT4_VERSION@ +instrumentationTestVersion = @INSTRUMENTATION_TEST_VERSION@ -junitPlatformVersion = @JUNIT_PLATFORM_VERSION@ -junitJupiterVersion = @JUNIT_JUPITER_VERSION@ -junitVintageVersion = @JUNIT_VINTAGE_VERSION@ +junitPlatformVersion = @JUNIT_PLATFORM_VERSION@ +junitJupiterVersion = @JUNIT_JUPITER_VERSION@ +junitVintageVersion = @JUNIT_VINTAGE_VERSION@ diff --git a/gradle.properties b/gradle.properties index bcafc0d7..fbdb9a8c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -39,6 +39,12 @@ DCENDENTS_MAVEN_PLUGIN_VERSION = 2.0 GROOVY_VERSION = 3.0.0-alpha-1 KOTLIN_VERSION = 1.2.0 +# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +# A Note to my forgetful self: +# +# When updating these values, make sure +# to always update the Travis CI .yml config, too +# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! # Android Environment (common) COMPILE_SDK_VERSION = android-27 BUILD_TOOLS_VERSION = 27.0.1 From aff11c910425ad496005b346f9ec9d472e5691e0 Mon Sep 17 00:00:00 2001 From: Marcel Schnelle Date: Sun, 3 Dec 2017 21:00:55 +0900 Subject: [PATCH 02/12] Move Jacoco-related DSL to the new format --- .../plugins/junit5/BasePluginSpec.groovy | 55 +++++++++ .../AndroidJUnitPlatformExtension.groovy | 111 ++++++++++++++++++ .../plugins/junit5/ExtensionProxy.groovy | 2 +- .../gradle/plugins/junit5/LogUtils.groovy | 26 +++- .../gradle/plugins/junit5/Dependencies.kt | 17 ++- .../gradle/plugins/junit5/Extensions.kt | 17 +-- .../gradle/plugins/junit5/Plugin.kt | 3 - .../gradle/plugins/junit5/tasks/Jacoco.kt | 25 ++-- 8 files changed, 218 insertions(+), 38 deletions(-) diff --git a/android-junit5-tests/testCommon/src/test/groovy/de/mannodermaus/gradle/plugins/junit5/BasePluginSpec.groovy b/android-junit5-tests/testCommon/src/test/groovy/de/mannodermaus/gradle/plugins/junit5/BasePluginSpec.groovy index 62ebb708..66d7d975 100644 --- a/android-junit5-tests/testCommon/src/test/groovy/de/mannodermaus/gradle/plugins/junit5/BasePluginSpec.groovy +++ b/android-junit5-tests/testCommon/src/test/groovy/de/mannodermaus/gradle/plugins/junit5/BasePluginSpec.groovy @@ -377,6 +377,61 @@ abstract class BasePluginSpec extends Specification { } } + project.junitPlatform { + jacoco { + xml { + enabled false + destination project.file("build/other-jacoco-folder/xml") + } + html { + enabled false + destination project.file("build/html-reports/jacoco") + } + csv { + enabled true + destination project.file("build/CSVISDABEST") + } + } + } + + project.evaluate() + + then: + // These statements automatically assert the existence of the tasks, + // and raise an Exception if absent + def runDebug = project.tasks.getByName("jacocoTestReportDebug") as AndroidJUnit5JacocoReport + def runRelease = project.tasks.getByName("jacocoTestReportRelease") + def runStaging = project.tasks.getByName("jacocoTestReportStaging") + def runAll = project.tasks.getByName("jacocoTestReport") + + // Assert that dependency chain is valid + assert runAll.getDependsOn().containsAll([runDebug, runRelease, runStaging]) + + // Assert report configuration parameters + assert runDebug.reports.xml.enabled == false + assert runDebug.reports.xml.destination.path.endsWith("build/other-jacoco-folder/xml") + assert runDebug.reports.html.enabled == false + assert runDebug.reports.html.destination.path.endsWith("build/html-reports/jacoco") + assert runDebug.reports.csv.enabled == true + assert runDebug.reports.csv.destination.path.endsWith("build/CSVISDABEST") + } + + // FIXME Deprecated. Remove test once APIs are deleted + @SuppressWarnings("GroovyPointlessBoolean") + def "Application: Jacoco Integration Using Old Configuration Parameters"() { + when: + Project project = factory.newProject(rootProject()) + .asAndroidApplication() + .applyJunit5Plugin() + .applyJacocoPlugin() + .build() + + project.android { + buildTypes { + staging {} + } + } + project.junitPlatform { jacoco { xmlReport false diff --git a/android-junit5/src/main/groovy/de/mannodermaus/gradle/plugins/junit5/AndroidJUnitPlatformExtension.groovy b/android-junit5/src/main/groovy/de/mannodermaus/gradle/plugins/junit5/AndroidJUnitPlatformExtension.groovy index 8cda07a4..6ea0e79f 100644 --- a/android-junit5/src/main/groovy/de/mannodermaus/gradle/plugins/junit5/AndroidJUnitPlatformExtension.groovy +++ b/android-junit5/src/main/groovy/de/mannodermaus/gradle/plugins/junit5/AndroidJUnitPlatformExtension.groovy @@ -17,8 +17,11 @@ import javax.annotation.Nullable class AndroidJUnitPlatformExtension extends JUnitPlatformExtension { + private final Project project + AndroidJUnitPlatformExtension(Project project) { super(project) + this.project = project } /** The version of JUnit Jupiter to use.*/ @@ -39,6 +42,8 @@ class AndroidJUnitPlatformExtension extends JUnitPlatformExtension { * - systemProperties */ boolean applyDefaultTestOptions = true + /* Integration of Instrumentation Tests */ + /** * Options for controlling instrumentation test execution with JUnit 5. * @@ -73,4 +78,110 @@ class AndroidJUnitPlatformExtension extends JUnitPlatformExtension { /** The version of the instrumentation companion library to use. */ @Nullable String version } + + /* Integration of Jacoco Reporting */ + + /** + * Options for controlling Jacoco reporting.*/ + private final JacocoOptions jacoco = new JacocoOptions(project) + + /** + * Configures Jacoco reporting options.*/ + void jacoco(Closure closure) { + ConfigureUtil.configure(closure, jacoco) + } + + /** + * Configures Jacoco reporting options.*/ + JacocoOptions getJacoco() { return jacoco } + + /** + * Options for controlling Jacoco reporting.*/ + static class JacocoOptions { + + private final Project project + private final Report html + private final Report csv + private final Report xml + + JacocoOptions(Project project) { + this.project = project + this.html = new Report() + this.csv = new Report() + this.xml = new Report() + } + + void html(Closure closure) { + ConfigureUtil.configure(closure, html) + } + + Report getHtml() { + return html + } + + void csv(Closure closure) { + ConfigureUtil.configure(closure, csv) + } + + Report getCsv() { + return csv + } + + void xml(Closure closure) { + ConfigureUtil.configure(closure, xml) + } + + Report getXml() { + return xml + } + + // FIXME DEPRECATED --------------------------------------------------------------- + def htmlReport(boolean state) { + logDeprecationWarning("htmlReport", "html.enabled") + html.enabled = state + } + + def csvReport(boolean state) { + logDeprecationWarning("csvReport", "csv.enabled") + csv.enabled = state + } + + def xmlReport(boolean state) { + logDeprecationWarning("xmlReport", "xml.enabled") + xml.enabled = state + } + + private def logDeprecationWarning(String dontUse, String useInstead) { + LogUtils.agpStyleLog(project.logger, + LogUtils.Level.WARNING, + "Accessing the Jacoco property '$dontUse' for JUnit 5 configuration " + "is deprecated and will be removed in a future version. Please use '$useInstead' instead") + } + + // END DEPRECATED ----------------------------------------------------------------- + + class Report { + + private boolean enabled = true + + @Nullable + private File destination + + void enabled(boolean state) { + this.enabled = state + } + + boolean isEnabled() { + return enabled + } + + void destination(File destination) { + this.destination = destination + } + + @Nullable + File getDestination() { + return destination + } + } + } } diff --git a/android-junit5/src/main/groovy/de/mannodermaus/gradle/plugins/junit5/ExtensionProxy.groovy b/android-junit5/src/main/groovy/de/mannodermaus/gradle/plugins/junit5/ExtensionProxy.groovy index ee8f4458..a39938ef 100644 --- a/android-junit5/src/main/groovy/de/mannodermaus/gradle/plugins/junit5/ExtensionProxy.groovy +++ b/android-junit5/src/main/groovy/de/mannodermaus/gradle/plugins/junit5/ExtensionProxy.groovy @@ -36,6 +36,6 @@ class ExtensionProxy { } private def logWarning() { - LogUtils.warning(project, warning) + LogUtils.agpStyleLog(project.logger, LogUtils.Level.WARNING, warning) } } diff --git a/android-junit5/src/main/groovy/de/mannodermaus/gradle/plugins/junit5/LogUtils.groovy b/android-junit5/src/main/groovy/de/mannodermaus/gradle/plugins/junit5/LogUtils.groovy index 954516c5..47e16034 100644 --- a/android-junit5/src/main/groovy/de/mannodermaus/gradle/plugins/junit5/LogUtils.groovy +++ b/android-junit5/src/main/groovy/de/mannodermaus/gradle/plugins/junit5/LogUtils.groovy @@ -1,14 +1,30 @@ package de.mannodermaus.gradle.plugins.junit5 -import org.gradle.api.Project +import org.gradle.api.logging.Logger /** * Note: This is in Groovy until no other code in this language - * is using it anymore, or no more groovy files remain. - */ + * is using it anymore, or no more groovy files remain.*/ class LogUtils { - static def warning(Project project, String message) { - project.logger.warn("AGPBI: {\"kind\":\"warning\",\"text\":\"$message\"}") + static def agpStyleLog(Logger logger, Level level, String message) { + def fullMessage = "AGPBI: {\"kind\":\"$level.tag\",\"text\":\"$message\"}" + + if (level == Level.WARNING) { + logger.warn(fullMessage) + } else { + logger.info(fullMessage) + } + } + + enum Level { + INFO("info"), + WARNING("warning"); + + private final String tag + + Level(String tag) { + this.tag = tag + } } } diff --git a/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/Dependencies.kt b/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/Dependencies.kt index 1ab499f4..498bab83 100644 --- a/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/Dependencies.kt +++ b/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/Dependencies.kt @@ -1,9 +1,11 @@ package de.mannodermaus.gradle.plugins.junit5 +import de.mannodermaus.gradle.plugins.junit5.LogUtils.Level import groovy.lang.Closure import org.gradle.api.Project import org.gradle.api.ProjectConfigurationException import org.gradle.api.artifacts.Dependency +import org.gradle.api.logging.Logger import java.util.Properties /* @@ -13,9 +15,11 @@ import java.util.Properties /* Extensions */ -private fun Project.logDeprecationWarning(oldName: String, newName: String) { - LogUtils.warning(this, "The JUnit 5 dependency on '$oldName' " + - "is deprecated and will be removed in a future version. Please use '$newName' instead!") +private fun Logger.replacementWarning(oldName: String, newName: String) { + this.agpStyleLog( + message = "The JUnit 5 dependency on '$oldName' " + + "is deprecated and will be removed in a future version. Please use '$newName' instead!", + level = Level.WARNING) } /* Types */ @@ -103,13 +107,14 @@ class JUnit5DependencyHandler( // "dependencies.junit5Params()" is the old way to specify parameterized tests project.dependencies.ext["junit5Params"] = Callable0 { - project.logDeprecationWarning(oldName = "junit5Params()", newName = "junit5.parameterized()") + project.logger.replacementWarning(oldName = "junit5Params()", + newName = "junit5.parameterized()") this.parameterized() } // "dependencies.junit5EmbeddedRuntime()" is the old way to specify the embedded runtime project.dependencies.ext["junit5EmbeddedRuntime"] = Callable0 { - project.logDeprecationWarning( + project.logger.replacementWarning( oldName = "junit5EmbeddedRuntime()", newName = "junit5.unitTestsRuntime()") this.unitTestsRuntime() @@ -119,7 +124,7 @@ class JUnit5DependencyHandler( // "dependencies.junit5()" is the old way to specify unit tests @Suppress("MemberVisibilityCanPrivate") operator fun invoke(): List { - project.logDeprecationWarning(oldName = "junit5()", newName = "junit5.unitTests()") + project.logger.replacementWarning(oldName = "junit5()", newName = "junit5.unitTests()") return this.unitTests() } diff --git a/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/Extensions.kt b/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/Extensions.kt index 27e4d441..2719ef51 100644 --- a/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/Extensions.kt +++ b/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/Extensions.kt @@ -5,7 +5,8 @@ import com.android.build.gradle.api.BaseVariant import com.android.build.gradle.api.UnitTestVariant import com.android.build.gradle.internal.api.TestedVariant import com.android.build.gradle.internal.dsl.TestOptions -import de.mannodermaus.gradle.plugins.junit5.tasks.AndroidJUnit5JacocoReport +import de.mannodermaus.gradle.plugins.junit5.LogUtils.Level +import de.mannodermaus.gradle.plugins.junit5.LogUtils.Level.INFO import de.mannodermaus.gradle.plugins.junit5.tasks.AndroidJUnit5UnitTest import groovy.lang.Closure import org.gradle.api.GradleException @@ -78,10 +79,6 @@ val FiltersExtension.packages val FiltersExtension.engines get() = extensionByName(ENGINES_EXTENSION_NAME) -@Deprecated(message = "will be removed") -val AndroidJUnitPlatformExtension.jacoco - get() = extensionByName(JACOCO_EXTENSION_NAME) - val TestOptions.junitPlatform get() = extensionByName(EXTENSION_NAME) @@ -91,6 +88,12 @@ val Project.jacoco val AndroidJUnit5UnitTest.jacoco get() = extensionByName("jacoco") +/* Extensions for Gradle */ + +fun Logger.agpStyleLog(message: String, level: Level = INFO) { + LogUtils.agpStyleLog(this, level, message) +} + /* Interoperability layer for Gradle */ /** @@ -206,7 +209,7 @@ fun Project.withDependencies(defaults: Properties, config: (Versions) -> Any): A * Multi-language functional construct, * mapped to Groovy's dynamic Closures as well as Kotlin's invoke syntax. * - * A [Callable] can be invoked with the short-hand + * A [Callable0] can be invoked with the short-hand * function syntax from both Kotlin & Groovy: * *
@@ -232,7 +235,7 @@ class Callable0(private val body: () -> R) : Closure(null) {
  * Multi-language functional construct,
  * mapped to Groovy's dynamic Closures as well as Kotlin's invoke syntax.
  *
- * A [Callable] can be invoked with the short-hand
+ * A [Callable1] can be invoked with the short-hand
  * function syntax from both Kotlin & Groovy:
  *
  * 
diff --git a/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/Plugin.kt b/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/Plugin.kt
index 1990d658..f108aad3 100644
--- a/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/Plugin.kt
+++ b/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/Plugin.kt
@@ -48,9 +48,6 @@ class AndroidJUnitPlatformPlugin : Plugin {
             filters.extend(TAGS_EXTENSION_NAME)
             filters.extend(ENGINES_EXTENSION_NAME)
           }
-
-          // TODO Deprecate & use actual Jacoco properties
-          ju5.extend(JACOCO_EXTENSION_NAME)
         }
 
     // FIXME Deprecated --------------------------------------------------------------------------------
diff --git a/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/tasks/Jacoco.kt b/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/tasks/Jacoco.kt
index b3c4ddfa..ccbf00aa 100644
--- a/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/tasks/Jacoco.kt
+++ b/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/tasks/Jacoco.kt
@@ -17,6 +17,7 @@ private const val GROUP_REPORTING = "reporting"
  * Jacoco Test Reporting Task connected to a variant-aware JUnit 5 task.
  * Required to be "open" in order for Groovy's proxy magic to do its thing.
  */
+@Suppress("MemberVisibilityCanPrivate")
 open class AndroidJUnit5JacocoReport : JacocoReport() {
 
   companion object {
@@ -28,18 +29,6 @@ open class AndroidJUnit5JacocoReport : JacocoReport() {
     }
   }
 
-  /**
-   * Configuration exposed to consumers
-   */
-  open class Extension {
-    /** Generate a test coverage report in CSV */
-    var csvReport = true
-    /** Generate a test coverage report in XML */
-    var xmlReport = true
-    /** Generate a test coverage report in HTML */
-    var htmlReport = true
-  }
-
   /**
    * Configuration closure for an Android JUnit5 Jacoco Report task.
    */
@@ -70,10 +59,14 @@ open class AndroidJUnit5JacocoReport : JacocoReport() {
 
       // Apply JUnit 5 configuration parameters
       val junit5Jacoco = project.junit5.jacoco
-      reportTask.reports.apply {
-        csv.isEnabled = junit5Jacoco.csvReport
-        html.isEnabled = junit5Jacoco.htmlReport
-        xml.isEnabled = junit5Jacoco.xmlReport
+      val allReports = listOf(
+          junit5Jacoco.csv to reportTask.reports.csv,
+          junit5Jacoco.xml to reportTask.reports.xml,
+          junit5Jacoco.html to reportTask.reports.html)
+
+      allReports.forEach { (from, to) ->
+        to.isEnabled = from.isEnabled
+        from.destination?.let { to.destination = it }
       }
 
       project.logger.junit5Info(

From ff317708b54e4332478c50724c6416a0628b8aa0 Mon Sep 17 00:00:00 2001
From: Marcel Schnelle 
Date: Sun, 3 Dec 2017 21:30:57 +0900
Subject: [PATCH 03/12] Move instrumentationTest "enabled" property to function
 invocation as well

---
 .../plugins/junit5/BasePluginSpec.groovy      |  8 +++----
 .../AndroidJUnitPlatformExtension.groovy      | 23 ++++++++++++++++---
 2 files changed, 24 insertions(+), 7 deletions(-)

diff --git a/android-junit5-tests/testCommon/src/test/groovy/de/mannodermaus/gradle/plugins/junit5/BasePluginSpec.groovy b/android-junit5-tests/testCommon/src/test/groovy/de/mannodermaus/gradle/plugins/junit5/BasePluginSpec.groovy
index 66d7d975..79411743 100644
--- a/android-junit5-tests/testCommon/src/test/groovy/de/mannodermaus/gradle/plugins/junit5/BasePluginSpec.groovy
+++ b/android-junit5-tests/testCommon/src/test/groovy/de/mannodermaus/gradle/plugins/junit5/BasePluginSpec.groovy
@@ -137,7 +137,7 @@ abstract class BasePluginSpec extends Specification {
         vintageVersion = "1.2.3"
 
         instrumentationTests {
-          enabled = true
+          enabled true
           version = "4.8.15"
         }
       }
@@ -563,7 +563,7 @@ abstract class BasePluginSpec extends Specification {
 
     project.android {
       testOptions.junitPlatform.instrumentationTests {
-        enabled = false
+        enabled false
       }
     }
 
@@ -583,7 +583,7 @@ abstract class BasePluginSpec extends Specification {
 
     project.android {
       testOptions.junitPlatform.instrumentationTests {
-        enabled = true
+        enabled true
       }
     }
 
@@ -608,7 +608,7 @@ abstract class BasePluginSpec extends Specification {
       }
 
       testOptions.junitPlatform.instrumentationTests {
-        enabled = true
+        enabled true
       }
     }
 
diff --git a/android-junit5/src/main/groovy/de/mannodermaus/gradle/plugins/junit5/AndroidJUnitPlatformExtension.groovy b/android-junit5/src/main/groovy/de/mannodermaus/gradle/plugins/junit5/AndroidJUnitPlatformExtension.groovy
index 6ea0e79f..19359087 100644
--- a/android-junit5/src/main/groovy/de/mannodermaus/gradle/plugins/junit5/AndroidJUnitPlatformExtension.groovy
+++ b/android-junit5/src/main/groovy/de/mannodermaus/gradle/plugins/junit5/AndroidJUnitPlatformExtension.groovy
@@ -39,8 +39,17 @@ class AndroidJUnitPlatformExtension extends JUnitPlatformExtension {
    * by a "testOptions.unitTests.all" closure:
    *
    * - jvmArgs
-   * - systemProperties */
-  boolean applyDefaultTestOptions = true
+   * - systemProperties
+   * - environment variables */
+  private boolean applyDefaultTestOptions = true
+
+  void applyDefaultTestOptions(boolean enabled) {
+    this.applyDefaultTestOptions = enabled
+  }
+
+  boolean getApplyDefaultTestOptions() {
+    return applyDefaultTestOptions
+  }
 
   /* Integration of Instrumentation Tests */
 
@@ -73,10 +82,18 @@ class AndroidJUnitPlatformExtension extends JUnitPlatformExtension {
   static class InstrumentationTestOptions {
 
     /** Whether or not to enable support for JUnit 5 instrumentation tests. */
-    boolean enabled = false
+    private boolean enabled = false
 
     /** The version of the instrumentation companion library to use. */
     @Nullable String version
+
+    void enabled(boolean state) {
+      this.enabled = state
+    }
+
+    boolean getEnabled() {
+      return enabled
+    }
   }
 
   /* Integration of Jacoco Reporting */

From 1e92da3ccc85de8e5fd7d298038970394925e764 Mon Sep 17 00:00:00 2001
From: Marcel Schnelle 
Date: Sun, 3 Dec 2017 21:52:57 +0900
Subject: [PATCH 04/12] Update renamed Gradle property

---
 .../de/mannodermaus/gradle/plugins/junit5/testenv.properties    | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/android-junit5-tests/testCommon/src/test/resources/de/mannodermaus/gradle/plugins/junit5/testenv.properties b/android-junit5-tests/testCommon/src/test/resources/de/mannodermaus/gradle/plugins/junit5/testenv.properties
index da393c00..fa9bb673 100644
--- a/android-junit5-tests/testCommon/src/test/resources/de/mannodermaus/gradle/plugins/junit5/testenv.properties
+++ b/android-junit5-tests/testCommon/src/test/resources/de/mannodermaus/gradle/plugins/junit5/testenv.properties
@@ -2,5 +2,5 @@
 # (Populated from a Gradle "process resources" task)
 compileSdkVersion = @COMPILE_SDK_VERSION@
 buildToolsVersion = @BUILD_TOOLS_VERSION@
-minSdkVersion     = @MIN_SDK_VERSION@
+minSdkVersion     = @SAMPLE_MIN_SDK_VERSION@
 targetSdkVersion  = @TARGET_SDK_VERSION@

From c20f817cd1e006358dbe6a5ed55ecc60e623cb36 Mon Sep 17 00:00:00 2001
From: Marcel Schnelle 
Date: Sun, 3 Dec 2017 22:07:47 +0900
Subject: [PATCH 05/12] Sample module still referred to old property

---
 sample/build.gradle | 8 +-------
 1 file changed, 1 insertion(+), 7 deletions(-)

diff --git a/sample/build.gradle b/sample/build.gradle
index 3e35cd30..cd5f8862 100644
--- a/sample/build.gradle
+++ b/sample/build.gradle
@@ -14,17 +14,11 @@ apply plugin: "de.mannodermaus.android-junit5"
 apply plugin: "jacoco"
 
 android {
-  // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-  // A Note to my forgetful self:
-  //
-  // When updating these values, make sure
-  // to always update the Travis CI .yml config, too
-  // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
   compileSdkVersion COMPILE_SDK_VERSION
 
   defaultConfig {
     applicationId "de.mannodermaus.junit5.sample"
-    minSdkVersion MIN_SDK_VERSION
+    minSdkVersion SAMPLE_MIN_SDK_VERSION
     targetSdkVersion TARGET_SDK_VERSION
     versionCode 1
     versionName "1.0"

From 1b7d054c4f71e02b2e0384afcdde40a0fbc6c63b Mon Sep 17 00:00:00 2001
From: Marcel Schnelle 
Date: Sun, 3 Dec 2017 22:29:06 +0900
Subject: [PATCH 06/12] Rename travis dir

---
 .travis.yml | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 8b9cc921..e02a4d32 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -44,9 +44,9 @@ after_success:
   - ./scripts/deploy_snapshot.sh
 
 after_failure:
-- cat $TRAVIS_BUILD_DIR/tests/testCommon/build/reports/tests/test/index.html
-- cat $TRAVIS_BUILD_DIR/tests/testAgp2x/build/reports/tests/test/index.html
-- cat $TRAVIS_BUILD_DIR/tests/testAgp3x/build/reports/tests/test/index.html
+- cat $TRAVIS_BUILD_DIR/android-junit5-tests/testCommon/build/reports/tests/test/index.html
+- cat $TRAVIS_BUILD_DIR/android-junit5-tests/testAgp2x/build/reports/tests/test/index.html
+- cat $TRAVIS_BUILD_DIR/android-junit5-tests/testAgp3x/build/reports/tests/test/index.html
 
 deploy:
   provider: script

From 378990d05191810fe7a89f8860cdc6092ccb70b8 Mon Sep 17 00:00:00 2001
From: Marcel Schnelle 
Date: Sun, 3 Dec 2017 22:41:06 +0900
Subject: [PATCH 07/12] Fix testenv

---
 .travis.yml                                                  | 5 -----
 android-junit5-tests/common.gradle                           | 1 +
 .../de/mannodermaus/gradle/plugins/junit5/testenv.properties | 2 +-
 3 files changed, 2 insertions(+), 6 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index e02a4d32..c57fc1cb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -43,11 +43,6 @@ script: ./gradlew check --stacktrace
 after_success:
   - ./scripts/deploy_snapshot.sh
 
-after_failure:
-- cat $TRAVIS_BUILD_DIR/android-junit5-tests/testCommon/build/reports/tests/test/index.html
-- cat $TRAVIS_BUILD_DIR/android-junit5-tests/testAgp2x/build/reports/tests/test/index.html
-- cat $TRAVIS_BUILD_DIR/android-junit5-tests/testAgp3x/build/reports/tests/test/index.html
-
 deploy:
   provider: script
   script: ./scripts/deploy_release.sh
diff --git a/android-junit5-tests/common.gradle b/android-junit5-tests/common.gradle
index f032f66d..ef278fb1 100644
--- a/android-junit5-tests/common.gradle
+++ b/android-junit5-tests/common.gradle
@@ -35,6 +35,7 @@ tasks.withType(WriteClasspathResource).all {
 
 test.testLogging {
   events "passed", "skipped", "failed"
+  exceptionFormat = "full"
 }
 
 dependencies {
diff --git a/android-junit5-tests/testCommon/src/test/resources/de/mannodermaus/gradle/plugins/junit5/testenv.properties b/android-junit5-tests/testCommon/src/test/resources/de/mannodermaus/gradle/plugins/junit5/testenv.properties
index fa9bb673..da393c00 100644
--- a/android-junit5-tests/testCommon/src/test/resources/de/mannodermaus/gradle/plugins/junit5/testenv.properties
+++ b/android-junit5-tests/testCommon/src/test/resources/de/mannodermaus/gradle/plugins/junit5/testenv.properties
@@ -2,5 +2,5 @@
 # (Populated from a Gradle "process resources" task)
 compileSdkVersion = @COMPILE_SDK_VERSION@
 buildToolsVersion = @BUILD_TOOLS_VERSION@
-minSdkVersion     = @SAMPLE_MIN_SDK_VERSION@
+minSdkVersion     = @MIN_SDK_VERSION@
 targetSdkVersion  = @TARGET_SDK_VERSION@

From c24cb0a0449b60beb7ecd62181981d689e9ba8e1 Mon Sep 17 00:00:00 2001
From: Marcel Schnelle 
Date: Sun, 3 Dec 2017 23:21:21 +0900
Subject: [PATCH 08/12] Upgraded README with the new info

---
 README.md | 157 ++++++++++++++++++++++++++++++++++--------------------
 1 file changed, 100 insertions(+), 57 deletions(-)

diff --git a/README.md b/README.md
index cfc5fd9d..2aadd7e4 100644
--- a/README.md
+++ b/README.md
@@ -4,51 +4,132 @@
 
 A Gradle plugin that allows for the execution of [JUnit 5][junit5gh] tests in Android environments.
 
+## Why a separate plugin?
+
+The JUnit Platform team provides a Gradle plugin for running JUnit 5 on the JVM. However,
+this plugin is tailored to the needs of a "purely Java" application, and doesn't work in
+the context of the multi-variant world that we live in on Android. Therefore, this plugin was born.
+
+It configures a `junitPlatformTest` task for each registered build variant of a project.
+Furthermore, it automatically attaches both the Jupiter & Vintage Engines
+during the execution phase of your tests as well, so there's very little configuration
+necessary to get your project up-and-running on the JUnit Platform.
+
+Instructions on how to write JUnit 5 tests can be found [in their User Guide][junit5ug].
+Furthermore, this repository provides a small showcase of the functionality provided by JUnit 5 [here][sampletests].
+
 ## Download
 
 ```groovy
 buildscript {
   dependencies {
-    classpath "de.mannodermaus.gradle.plugins:android-junit5:1.0.21"
+    classpath "de.mannodermaus.gradle.plugins:android-junit5:1.0.22"
   }
 }
 ```
 
 Snapshots of the development version are available through [Sonatype's `snapshots` repository][sonatyperepo].
 
-## Setup
+## Basic Setup
 
 ```groovy
 apply plugin: "com.android.application"
 apply plugin: "de.mannodermaus.android-junit5"
 
 dependencies {
-  testImplementation junit5()
+  // (Required) Writing and executing Unit Tests on the JUnit Platform.
+  testImplementation junit5.unitTests()
 
-  // (Optional) If you need "parameterized tests"
-  testImplementation junit5Params()
+  // (Optional) If you need "Parameterized Tests".
+  testImplementation junit5.parameterized()
     
-  // (Optional) For running tests inside Android Studio 3.x (see below for details)
-  testCompileOnly junit5EmbeddedRuntime()
+  // (Optional) For running tests inside Android Studio 3.x
+  // Please refer to the "Android Studio Workarounds" section for more insight on this.
+  testCompileOnly junit5.unitTestsRuntime()
+
+  // (Optional) Writing and executing Instrumented Tests with the JUnit Platform Runner.
+  //
+  // IMPORTANT:
+  // By declaring this dependency, you are required to use a minSdkVersion
+  // of at least 26, since the nature of JUnit 5 relies on APIs that aren't
+  // available on Android devices before then.
+  // Additionally, you are required to explicitly enable support for instrumented tests in the
+  // "junitPlatform" configuration closure (see the section below for details).
+  androidTestImplementation junit5.instrumentedTests()
 }
 ```
 
-## Usage
+## Configuration
 
-This plugin configures a `junitPlatformTest` task for each registered build variant of a project.
-It automatically attaches both the Jupiter & Vintage Engines during the execution phase of your tests as well.
+The plugin applies a configuration closure to your module's `android.testOptions`.
+Inside it, you can use [all properties available through the default JUnit 5 Gradle plugin](junit5config).
+However, there are a few more parameters that allow for more customization of the JUnit Platform
+in your Android project. These are detailed below, alongside their default values:
 
-More instructions on how to write JUnit 5 tests can be found [in their User Guide][junit5ug].
-Furthermore, this repository provides a small showcase of the functionality provided by JUnit 5 [here][sampletests].
+```groovy
+android {
+  testOptions {
+    // Configuration closure added by the plugin;
+    // all configurable parameters related to JUnit 5 can be found here
+    junitPlatform {
+      // The JUnit Jupiter dependency version to use
+      jupiterVersion "5.0.2"
+
+      // The JUnit Vintage Engine dependency version to use
+      vintageVersion "4.12.2"
+
+      // Whether or not JUnit 5 test tasks should be affected by
+      // JVM Arguments, System Properties & Environment Variables
+      // declared through "unitTests.all" closures
+      applyDefaultTestOptions true
+
+      // Options related to running instrumented tests with JUnit 5.
+      // This is an incubating feature which utilizes the backwards-compatibility
+      // of the JUnit Platform in order to extend the default Test Instrumentation Runner
+      // with new power. Because of their experimental nature and minSdkVersion requirement,
+      // they are turned off by default. If you enable them, you also have to specify
+      // the library dependency in your androidTest scope. Please refer to the "Instrumented Tests"
+      // section for more details.
+      instrumentationTests {
+        enabled false
+
+        // The Android-Instrumentation-Test dependency version to use
+        version "0.1.0"
+      }
+
+      // Configuration of companion tasks for JaCoCo Reports,
+      // associated with each JUnit 5 task generated by the plugin.
+      // Just like the companion tasks themselves, these properties
+      // will only have an effect if your module declares the "jacoco" plugin as well.
+      // For each of the available report types, you can toggle the availability
+      // and destination folders that they write to.
+      jacoco {
+        xml {
+          enabled true
+          destination project.file()
+        }
+        html {
+          enabled true
+          destination project.file()
+        }
+        csv {
+          enabled true
+          destination project.file()
+        }
+      }
+    }
+  }
+}
+```
 
 ## Android Studio Workarounds
 
 > **Note:**
-> 
+>
 > The following section deals with fixing Test Execution within **Android Studio 3**.
 > Running your JUnit 5 tests directly from Android Studio 2.3.3 and earlier **will not work**:
 > You will encounter an `AbstractMethodError` when trying to do so ([more information here][as2issue]).
-> 
+>
 > The cause of this error is similar in nature to the one described below, and related to outdated APIs.
 > Unlike that issue though, we can't fix the `AbstractMethodError` inside IntelliJ's internal runtime
 > in the same way. Therefore, please resort to using Gradle for unit testing in Android Studio 2.
@@ -73,52 +154,13 @@ To use this, add the following line alongside the other `junit5()` dependencies:
 
 ```groovy
 dependencies {
-  testCompileOnly junit5EmbeddedRuntime()
-}
-```
-
-## Extras
-
-### Override Dependency Versions
-
-Inside the configuration closure applied by the plugin, you can specify the same properties as you would
-for a Java-based project with the JUnit Platform Gradle plugin.
-However, there are some additional properties that you can apply:
-
-```groovy
-junitPlatform {
-  // The JUnit Jupiter dependency version to use; matches the platform's version by default
-  jupiterVersion "5.0.2"
-  // The JUnit Vintage Engine dependency version to use; matches the platform's version by default
-  vintageVersion "4.12.2"
-}
-```
-
-### JaCoCo Integration
-
-If the plugin detects the usage of [JaCoCo][jacoco] inside a project that it's being applied to,
-it will automatically configure additional tasks to report the unit test coverage
-of your application based on its JUnit 5 tests.
-There is no additional setup required to enable this behaviour.
-You can however customize the reports JaCoCo should generate.
-
-Configuration is applied through the `jacoco` clause inside the plugin's DSL:
-
-```groovy
-apply plugin: "jacoco"
-
-junitPlatform {
-  jacoco {
-    csvReport true
-    xmlReport true
-    htmlReport true
-  }
+  testCompileOnly junit5.unitTestsRuntime()
 }
 ```
 
 ## Licenses
 
-#### `android-junit5-embedded-runtime`
+#### android-junit5-embedded-runtime:
 
 ```
 Copyright 2000-2016 JetBrains s.r.o.
@@ -138,7 +180,7 @@ limitations under the License.
 
 See also the [full License text](android-junit5-embedded-runtime/LICENSE).
 
-#### Others:
+#### Everything else:
 
 ```
 Copyright 2017 Marcel Schnelle
@@ -161,7 +203,8 @@ See also the [full License text](LICENSE).
 
 
  [junit5gh]: https://github.com/junit-team/junit5
- [junit5ug]: http://junit.org/junit5/docs/current/user-guide
+ [junit5ug]: https://junit.org/junit5/docs/current/user-guide
+ [junit5config]: http://junit.org/junit5/docs/current/user-guide/#running-tests-build-gradle-junit-configure
  [travisci]: https://travis-ci.org/mannodermaus/android-junit5
  [as2issue]: https://github.com/mannodermaus/android-junit5/issues/19
  [jacoco]: http://www.eclemma.org/jacoco

From 2b1fae14e1b8d1f00daae7a4933c4898fc5adc0d Mon Sep 17 00:00:00 2001
From: Marcel Schnelle 
Date: Sun, 3 Dec 2017 23:24:30 +0900
Subject: [PATCH 09/12] Small brush-ups

---
 README.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index 2aadd7e4..ef63f601 100644
--- a/README.md
+++ b/README.md
@@ -50,7 +50,7 @@ dependencies {
   // (Optional) Writing and executing Instrumented Tests with the JUnit Platform Runner.
   //
   // IMPORTANT:
-  // By declaring this dependency, you are required to use a minSdkVersion
+  // By declaring this dependency, you have to use a minSdkVersion
   // of at least 26, since the nature of JUnit 5 relies on APIs that aren't
   // available on Android devices before then.
   // Additionally, you are required to explicitly enable support for instrumented tests in the
@@ -62,7 +62,7 @@ dependencies {
 ## Configuration
 
 The plugin applies a configuration closure to your module's `android.testOptions`.
-Inside it, you can use [all properties available through the default JUnit 5 Gradle plugin](junit5config).
+Inside it, you can use [all properties available through the default JUnit 5 Gradle plugin][junit5config].
 However, there are a few more parameters that allow for more customization of the JUnit Platform
 in your Android project. These are detailed below, alongside their default values:
 

From ebad7441cca0fb7deb1e515dc4ac6a8605383788 Mon Sep 17 00:00:00 2001
From: Marcel Schnelle 
Date: Sun, 3 Dec 2017 23:27:45 +0900
Subject: [PATCH 10/12] Even more small brush-ups

---
 README.md | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/README.md b/README.md
index ef63f601..aa134e11 100644
--- a/README.md
+++ b/README.md
@@ -30,7 +30,7 @@ buildscript {
 
 Snapshots of the development version are available through [Sonatype's `snapshots` repository][sonatyperepo].
 
-## Basic Setup
+## Setup
 
 ```groovy
 apply plugin: "com.android.application"
@@ -85,10 +85,10 @@ android {
 
       // Options related to running instrumented tests with JUnit 5.
       // This is an incubating feature which utilizes the backwards-compatibility
-      // of the JUnit Platform in order to extend the default Test Instrumentation Runner
-      // with new power. Because of their experimental nature and minSdkVersion requirement,
-      // they are turned off by default. If you enable them, you also have to specify
-      // the library dependency in your androidTest scope. Please refer to the "Instrumented Tests"
+      // of the JUnit Platform in order to enhance the default Test Instrumentation Runner
+      // with new power. However, because of their experimental nature and steep minSdkVersion requirement,
+      // they are turned off by default. If you choose to enable them, you also have to declare
+      // the library dependency in your androidTest scope. Please refer to the "Setup"
       // section for more details.
       instrumentationTests {
         enabled false

From 810daf14200c56edf5b02c3db3363948e548db28 Mon Sep 17 00:00:00 2001
From: Marcel Schnelle 
Date: Sun, 3 Dec 2017 23:29:24 +0900
Subject: [PATCH 11/12] ...and a typo

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index aa134e11..a51d5c18 100644
--- a/README.md
+++ b/README.md
@@ -55,7 +55,7 @@ dependencies {
   // available on Android devices before then.
   // Additionally, you are required to explicitly enable support for instrumented tests in the
   // "junitPlatform" configuration closure (see the section below for details).
-  androidTestImplementation junit5.instrumentedTests()
+  androidTestImplementation junit5.instrumentationTests()
 }
 ```
 

From f0ce9dc705b3a6d2d1b959aca1eb06380e4f680f Mon Sep 17 00:00:00 2001
From: Marcel Schnelle 
Date: Sun, 3 Dec 2017 23:41:46 +0900
Subject: [PATCH 12/12] Release next version

---
 gradle.properties | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/gradle.properties b/gradle.properties
index fbdb9a8c..e96a2cf2 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -12,7 +12,7 @@ LICENSE_NAME                                = Apache-2.0
 PLUGIN_GROUP_ID                             = de.mannodermaus.gradle.plugins
 PLUGIN_ARTIFACT_ID                          = android-junit5
 PLUGIN_DESCRIPTION                          = Unit Testing with JUnit 5 for Android.
-PLUGIN_RUNTIME_VERSION_NAME                 = 1.0.22-SNAPSHOT
+PLUGIN_RUNTIME_VERSION_NAME                 = 1.0.22
 
 # Artifact configuration (embedded-runtime)
 RUNTIME_GROUP_ID                            = de.mannodermaus.gradle.plugins
@@ -23,7 +23,7 @@ RUNTIME_DESCRIPTION                         = Mirror of IntelliJ IDEA's embedded
 INSTRUMENTATION_GROUP_ID                    = de.mannodermaus.junit5
 INSTRUMENTATION_ARTIFACT_ID                 = android-instrumentation-test
 INSTRUMENTATION_DESCRIPTION                 = Extensions for instrumented Android tests with JUnit 5.
-INSTRUMENTATION_VERSION_NAME                = 0.1.1-SNAPSHOT
+INSTRUMENTATION_VERSION_NAME                = 0.1.1
 INSTRUMENTATION_MIN_SDK_VERSION             = 26
 
 # Dependency versions (plugins)