From 336f381caaea3a4e7973b6a4729f78716c49230b Mon Sep 17 00:00:00 2001 From: Micah Jo Date: Fri, 15 Mar 2024 03:22:06 -0700 Subject: [PATCH] Add recipe for disabling tests (VariantBuilder) Bug: n/a Test: this is a test Change-Id: I002f9af4d4595b3cb560692ab290eb79a8dc8cd8 --- BUILD | 4 + recipes/disableTests/README.md | 42 ++++++++++ recipes/disableTests/app/build.gradle.kts | 36 +++++++++ .../app/src/main/AndroidManifest.xml | 17 ++++ .../build-logic/gradle.properties | 2 + .../build-logic/gradle/libs.versions.toml | 9 +++ .../build-logic/plugins/build.gradle.kts | 40 ++++++++++ .../src/main/kotlin/CheckTestStatusTask.kt | 53 +++++++++++++ .../plugins/src/main/kotlin/CustomPlugin.kt | 79 +++++++++++++++++++ .../build-logic/settings.gradle.kts | 33 ++++++++ recipes/disableTests/build.gradle.kts | 20 +++++ recipes/disableTests/gradle.properties | 23 ++++++ .../disableTests/gradle/libs.versions.toml | 7 ++ recipes/disableTests/recipe_metadata.toml | 37 +++++++++ recipes/disableTests/settings.gradle.kts | 35 ++++++++ .../plugins/src/main/kotlin/CustomPlugin.kt | 2 - 16 files changed, 437 insertions(+), 2 deletions(-) create mode 100644 recipes/disableTests/README.md create mode 100644 recipes/disableTests/app/build.gradle.kts create mode 100644 recipes/disableTests/app/src/main/AndroidManifest.xml create mode 100644 recipes/disableTests/build-logic/gradle.properties create mode 100644 recipes/disableTests/build-logic/gradle/libs.versions.toml create mode 100644 recipes/disableTests/build-logic/plugins/build.gradle.kts create mode 100644 recipes/disableTests/build-logic/plugins/src/main/kotlin/CheckTestStatusTask.kt create mode 100644 recipes/disableTests/build-logic/plugins/src/main/kotlin/CustomPlugin.kt create mode 100644 recipes/disableTests/build-logic/settings.gradle.kts create mode 100644 recipes/disableTests/build.gradle.kts create mode 100644 recipes/disableTests/gradle.properties create mode 100644 recipes/disableTests/gradle/libs.versions.toml create mode 100644 recipes/disableTests/recipe_metadata.toml create mode 100644 recipes/disableTests/settings.gradle.kts diff --git a/BUILD b/BUILD index 6a909033..7974b625 100644 --- a/BUILD +++ b/BUILD @@ -169,3 +169,7 @@ recipe_test( recipe_test( name = "appendToScopedArtifacts", ) + +recipe_test( + name = "disableTests", +) diff --git a/recipes/disableTests/README.md b/recipes/disableTests/README.md new file mode 100644 index 00000000..d4b672b4 --- /dev/null +++ b/recipes/disableTests/README.md @@ -0,0 +1,42 @@ +# Disable tests using the variant API + +This sample shows how to disable both unit tests and Android tests on a project using the variant API. These properties +are set on sub-types of [`VariantBuilder`](https://developer.android.com/reference/tools/gradle-api/current/com/android/build/api/variant/VariantBuilder), for both unit tests and Android tests. + +This recipe contains the following directories: + +| Module | Content | +|----------------------------|-------------------------------------------------------------| +| [build-logic](build-logic) | Contains the Project plugin that is the core of the recipe. | +| [app](app) | An Android application that has the plugin applied. | + +The [build-logic](build-logic) sub-project contains the [`CustomPlugin`](build-logic/plugins/src/main/kotlin/CustomPlugin.kt) and [`CheckTestStatusClass`](build-logic/plugins/src/main/kotlin/CheckTestStatusClass.kt). + +[`CustomPlugin`](build-logic/plugins/src/main/kotlin/CustomPlugin.kt) uses the [VariantBuilder] object to disable to the tests on different variants. These properties +are then passed into the [`CheckTestStatusTask`](build-logic/plugins/src/main/kotlin/CheckTestStatusTask.kt) to validate +they are turned off for each variant. Below is an example usage of disabling the Android test type: + +``` +androidComponents.beforeVariants(androidComponents.selector().withBuildType("debug")) { variantBuilder -> + (variantBuilder as? HasDeviceTestsBuilder)?.deviceTests?.get(DeviceTestBuilder.ANDROID_TEST_TYPE)?.enable = false +} +``` + +The variant builder must be a sub-type of `HasDeviceTestsBuilder` here to access the property. In this example, we +query the `deviceTests` property for the Android tests, and disable them for the debug build type. + +For host tests, the sub-type `HasHostTestsBuilder` is required, and we query the host test types for the unit test +entry. This is disabled using the following snippet: + +``` +androidComponents.beforeVariants(androidComponents.selector().withBuildType("release")) { variantBuilder -> + (variantBuilder as? HasHostTestsBuilder)?.hostTests?.get(HostTestBuilder.UNIT_TEST_TYPE)?.enable = false +} +``` + +This will disable the unit tests for the release build type. + +## To Run +To execute example you need to enter command: + +`./gradlew :app:checkDebugTestStatus` and `./gradlew :app:checkReleaseTestStatus` diff --git a/recipes/disableTests/app/build.gradle.kts b/recipes/disableTests/app/build.gradle.kts new file mode 100644 index 00000000..7ab0a1ab --- /dev/null +++ b/recipes/disableTests/app/build.gradle.kts @@ -0,0 +1,36 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) + id("android.recipes.disable_tests") +} + +android { + namespace = "com.example.android.recipes.disable_tests" + compileSdk = $COMPILE_SDK + defaultConfig { + minSdk = $MINIMUM_SDK + targetSdk = $COMPILE_SDK + } +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } +} \ No newline at end of file diff --git a/recipes/disableTests/app/src/main/AndroidManifest.xml b/recipes/disableTests/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..8b7fed3d --- /dev/null +++ b/recipes/disableTests/app/src/main/AndroidManifest.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/recipes/disableTests/build-logic/gradle.properties b/recipes/disableTests/build-logic/gradle.properties new file mode 100644 index 00000000..3dcf88f0 --- /dev/null +++ b/recipes/disableTests/build-logic/gradle.properties @@ -0,0 +1,2 @@ +# Gradle properties are not passed to included builds https://github.com/gradle/gradle/issues/2534 +org.gradle.parallel=true diff --git a/recipes/disableTests/build-logic/gradle/libs.versions.toml b/recipes/disableTests/build-logic/gradle/libs.versions.toml new file mode 100644 index 00000000..d009d769 --- /dev/null +++ b/recipes/disableTests/build-logic/gradle/libs.versions.toml @@ -0,0 +1,9 @@ +[versions] +androidGradlePlugin = $AGP_VERSION +kotlin = $KOTLIN_VERSION + +[libraries] +android-gradlePlugin-api = { group = "com.android.tools.build", name = "gradle-api", version.ref = "androidGradlePlugin" } + +[plugins] +kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } \ No newline at end of file diff --git a/recipes/disableTests/build-logic/plugins/build.gradle.kts b/recipes/disableTests/build-logic/plugins/build.gradle.kts new file mode 100644 index 00000000..864c9920 --- /dev/null +++ b/recipes/disableTests/build-logic/plugins/build.gradle.kts @@ -0,0 +1,40 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + `java-gradle-plugin` + alias(libs.plugins.kotlin.jvm) +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } +} + +dependencies { + compileOnly(libs.android.gradlePlugin.api) + implementation(gradleKotlinDsl()) +} + +gradlePlugin { + plugins { + create("customPlugin") { + id = "android.recipes.disable_tests" + implementationClass = "CustomPlugin" + } + } +} diff --git a/recipes/disableTests/build-logic/plugins/src/main/kotlin/CheckTestStatusTask.kt b/recipes/disableTests/build-logic/plugins/src/main/kotlin/CheckTestStatusTask.kt new file mode 100644 index 00000000..d35ad2dd --- /dev/null +++ b/recipes/disableTests/build-logic/plugins/src/main/kotlin/CheckTestStatusTask.kt @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import org.gradle.api.DefaultTask +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.TaskAction +import java.lang.RuntimeException + +abstract class CheckTestStatusTask : DefaultTask() { + + // In order for the task to be up-to-date when the inputs have not changed, + // the task must declare an output, even if it's not used. Tasks with no + // output are always run regardless of whether the inputs changed + @get:OutputDirectory + abstract val output: DirectoryProperty + + @get:Input + abstract val unitTestEnabled: Property + + @get:Input + abstract val androidTestEnabled: Property + + @get:Input + abstract val variantName: Property + + @TaskAction + fun taskAction() { + if (variantName.get() == "debug" && (!unitTestEnabled.get() || androidTestEnabled.get())) { + throw RuntimeException("The debug variant should have Android tests disabled.") + } + // The Android test is disabled by default for the release variant, so we can validate that + // both unit tests and Android tests are disabled + if (variantName.get() == "release" && (androidTestEnabled.get() || unitTestEnabled.get())) { + throw RuntimeException("The release variant should have unit tests disabled.") + } + } +} diff --git a/recipes/disableTests/build-logic/plugins/src/main/kotlin/CustomPlugin.kt b/recipes/disableTests/build-logic/plugins/src/main/kotlin/CustomPlugin.kt new file mode 100644 index 00000000..78b6a680 --- /dev/null +++ b/recipes/disableTests/build-logic/plugins/src/main/kotlin/CustomPlugin.kt @@ -0,0 +1,79 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import com.android.build.api.variant.AndroidComponentsExtension +import com.android.build.api.variant.DeviceTestBuilder +import com.android.build.api.variant.HasDeviceTests +import com.android.build.api.variant.HasDeviceTestsBuilder +import com.android.build.api.variant.HasHostTests +import com.android.build.api.variant.HasHostTestsBuilder +import com.android.build.api.variant.HostTestBuilder +import com.android.build.gradle.BasePlugin +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.register +import org.gradle.configurationcache.extensions.capitalized + +/** + * This custom plugin will register a callback that is applied to all variants for all Android plugins. + */ +class CustomPlugin : Plugin { + override fun apply(project: Project) { + + // Registers a callback on the Android base plugin. + // This allows the CustomPlugin to work whether it's applied before or after the base plugin. + project.plugins.withType(BasePlugin::class.java) { + + val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java) + + // Disable tests + // We will disable the Android tests for the debug variant, and disable unit tests for release + androidComponents.beforeVariants(androidComponents.selector().withBuildType("debug")) { variantBuilder -> + // Query the device tests by name to find the Android tests, and disable them + // This is not available on all sub types of Variant- only those implementing HasDeviceTestsBuilder + // supports device tests + (variantBuilder as? HasDeviceTestsBuilder)?.deviceTests?.get( + DeviceTestBuilder.ANDROID_TEST_TYPE + )?.enable = false + } + androidComponents.beforeVariants(androidComponents.selector().withBuildType("release")) { variantBuilder -> + // There are multiple host test types, and they can all be disabled, or they can be queried and + // disabled individually by name like below for unit tests + // This is not available on all sub types of Variant- only those implementing HasHostTestsBuilder + // supports host tests + (variantBuilder as? HasHostTestsBuilder)?.hostTests?.get(HostTestBuilder.UNIT_TEST_TYPE)?.enable = false + } + + // -- Verification -- + // the following is just to validate the recipe and is not actually part of the recipe itself + androidComponents.onVariants { variant -> + val taskName = "check${variant.name.capitalized()}TestStatus" + project.tasks.register(taskName) { + // If the unit tests are disabled, the unit test type will not be present in the host tests + unitTestEnabled.set( + (variant as? HasHostTests)?.hostTests?.get(HostTestBuilder.UNIT_TEST_TYPE) != null + ) + // If the Android tests are disabled, the Android test type will not be present in the device tests + androidTestEnabled.set( + (variant as? HasDeviceTests)?.deviceTests?.get(DeviceTestBuilder.ANDROID_TEST_TYPE) != null + ) + variantName.set(variant.name) + output.set(project.layout.buildDirectory.dir("intermediates/$taskName")) + } + } + } + } +} \ No newline at end of file diff --git a/recipes/disableTests/build-logic/settings.gradle.kts b/recipes/disableTests/build-logic/settings.gradle.kts new file mode 100644 index 00000000..e2e5e9e5 --- /dev/null +++ b/recipes/disableTests/build-logic/settings.gradle.kts @@ -0,0 +1,33 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +rootProject.name = "build-logic" + +pluginManagement { + repositories { + $AGP_REPOSITORY + $PLUGIN_REPOSITORIES + } +} + +dependencyResolutionManagement { + repositories { + $AGP_REPOSITORY + $DEPENDENCY_REPOSITORIES + } +} + +include(":plugins") diff --git a/recipes/disableTests/build.gradle.kts b/recipes/disableTests/build.gradle.kts new file mode 100644 index 00000000..68e631f2 --- /dev/null +++ b/recipes/disableTests/build.gradle.kts @@ -0,0 +1,20 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + alias(libs.plugins.android.application) apply false + alias(libs.plugins.kotlin.android) apply false +} diff --git a/recipes/disableTests/gradle.properties b/recipes/disableTests/gradle.properties new file mode 100644 index 00000000..55cce922 --- /dev/null +++ b/recipes/disableTests/gradle.properties @@ -0,0 +1,23 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true diff --git a/recipes/disableTests/gradle/libs.versions.toml b/recipes/disableTests/gradle/libs.versions.toml new file mode 100644 index 00000000..e2285f69 --- /dev/null +++ b/recipes/disableTests/gradle/libs.versions.toml @@ -0,0 +1,7 @@ +[versions] +androidGradlePlugin = $AGP_VERSION +kotlin = $KOTLIN_VERSION + +[plugins] +android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" } +kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } diff --git a/recipes/disableTests/recipe_metadata.toml b/recipes/disableTests/recipe_metadata.toml new file mode 100644 index 00000000..3849ef4e --- /dev/null +++ b/recipes/disableTests/recipe_metadata.toml @@ -0,0 +1,37 @@ +# optional (if present and non-blank) name to use in the index +indexName = "" +# optional (if present and non-blank) folder name to use when converting recipe in RELEASE mode +destinationFolder = "" + +description =""" + This sample shows how to use VariantBuilder properties to disable tests in a project + """ + +[agpVersion] +min = "8.7.0-alpha08" + +# Relevant Gradle tasks to run per recipe +[gradleTasks] +tasks = [ + ":app:checkDebugTestStatus", + ":app:checkReleaseTestStatus" +] + +# All the relevant metadata fields to create an index based on language/API/etc +[indexMetadata] +index = [ + "Call chains/androidComponents.beforeVariants {}", + "Call chains/androidComponents.onVariants {}", + "APIs/AndroidComponentsExtension.beforeVariants()", + "APIs/AndroidComponentsExtension.onVariants()", + "Call chains/HasHostTestsBuilder.hostTests.get().enable", + "Call chains/HasDeviceTestsBuilder.deviceTests.get().enable", + "Call chains/HasHostTests.hostTests.get()", + "Call chains/HasDeviceTests.deviceTests.get()", + "APIs/HasDeviceTestsBuilder.deviceTests", + "APIs/HasDeviceTests.deviceTests", + "APIs/HasHostTestsBuilder.hostTests", + "APIs/HasHostTests.hostTests", + "HostTestBuilder.UNIT_TEST_TYPE", + "DeviceTestBuilder.ANDROID_TEST_TYPE" +] diff --git a/recipes/disableTests/settings.gradle.kts b/recipes/disableTests/settings.gradle.kts new file mode 100644 index 00000000..608d62da --- /dev/null +++ b/recipes/disableTests/settings.gradle.kts @@ -0,0 +1,35 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +rootProject.name = "disableTests" + +pluginManagement { + includeBuild("build-logic") + repositories { + $AGP_REPOSITORY + $PLUGIN_REPOSITORIES + } +} + +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + $AGP_REPOSITORY + $DEPENDENCY_REPOSITORIES + } +} + +include(":app") diff --git a/recipes/transformDirectory/build-logic/plugins/src/main/kotlin/CustomPlugin.kt b/recipes/transformDirectory/build-logic/plugins/src/main/kotlin/CustomPlugin.kt index 08b16246..ad375322 100644 --- a/recipes/transformDirectory/build-logic/plugins/src/main/kotlin/CustomPlugin.kt +++ b/recipes/transformDirectory/build-logic/plugins/src/main/kotlin/CustomPlugin.kt @@ -17,12 +17,10 @@ import com.android.build.api.artifact.SingleArtifact import com.android.build.api.variant.ApplicationAndroidComponentsExtension import com.android.build.gradle.AppPlugin -import org.gradle.api.DefaultTask import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.file.DirectoryProperty import org.gradle.api.tasks.OutputDirectory -import org.gradle.api.tasks.TaskAction import org.gradle.kotlin.dsl.register import org.gradle.configurationcache.extensions.capitalized