From 48f5228c96b65023cb114e327454202dc26ead30 Mon Sep 17 00:00:00 2001 From: Marcel Schnelle Date: Tue, 30 Dec 2025 15:24:07 +0100 Subject: [PATCH 1/3] Introduce ktfmt --- build-logic/gradle/libs.versions.toml | 2 + plugin/android-junit5/build.gradle.kts | 202 +++++++------ .../junit5/AndroidJUnitPlatformPlugin.kt | 21 +- .../dsl/AndroidJUnitPlatformExtension.kt | 39 +-- .../plugins/junit5/dsl/FiltersExtension.kt | 49 +-- .../junit5/dsl/InstrumentationTestOptions.kt | 32 +- .../plugins/junit5/dsl/JacocoOptions.kt | 68 ++--- .../junit5/internal/config/Constants.kt | 4 +- .../config/JUnitPlatformTaskConfig.kt | 14 +- .../junit5/internal/config/PluginConfig.kt | 44 +-- .../plugins/junit5/internal/configure.kt | 82 +++--- .../extensions/ConfigurableReportExt.kt | 23 +- .../junit5/internal/extensions/LoggerExt.kt | 13 +- .../junit5/internal/extensions/MapExt.kt | 2 +- .../junit5/internal/extensions/ProjectExt.kt | 13 +- .../junit5/internal/extensions/StringExt.kt | 4 +- .../junit5/internal/extensions/VariantExt.kt | 24 +- .../internal/providers/DirectoryProvider.kt | 36 +-- .../providers/JavaDirectoryProvider.kt | 16 +- .../providers/KotlinDirectoryProvider.kt | 18 +- .../internal/usage/DependencyUsageDetector.kt | 38 ++- .../internal/utils/IncludeExcludeContainer.kt | 21 +- .../junit5/tasks/AndroidJUnit5JacocoReport.kt | 70 ++--- .../junit5/tasks/AndroidJUnit5WriteFilters.kt | 54 ++-- .../plugins/junit5/ConfigurationCacheTests.kt | 57 ++-- .../gradle/plugins/junit5/FunctionalTests.kt | 72 ++--- .../junit5/IncludeExcludeContainerTests.kt | 98 +++--- .../junit5/annotations/DisabledOnCI.kt | 3 +- .../junit5/plugin/AbstractProjectTests.kt | 41 ++- .../plugin/AgpConfigurationParameterTests.kt | 15 +- .../plugins/junit5/plugin/AgpFilterTests.kt | 121 +++----- .../plugin/AgpInstrumentationSupportTests.kt | 32 +- .../junit5/plugin/AgpJacocoBaseTests.kt | 66 +++-- .../plugin/AgpJacocoExclusionRuleTests.kt | 126 ++++---- .../junit5/plugin/AgpJacocoVariantTests.kt | 252 ++++++++-------- .../plugins/junit5/plugin/AgpProjectTests.kt | 6 +- .../gradle/plugins/junit5/plugin/AgpTests.kt | 23 +- .../plugins/junit5/plugin/AgpVariantTests.kt | 32 +- .../plugin/InstrumentationSupportTests.kt | 169 ++++++++--- .../plugin/TestProjectProviderExtension.kt | 5 +- .../junit5/plugin/WrongPluginUsageTests.kt | 15 +- .../tasks/AndroidJUnit5WriteFiltersTests.kt | 35 +-- .../gradle/plugins/junit5/util/GradleTruth.kt | 69 ++--- .../plugins/junit5/util/TestEnvironment.kt | 70 ++--- .../plugins/junit5/util/TestExtensions.kt | 57 ++-- .../plugins/junit5/util/TestKitTruth.kt | 47 ++- .../gradle/plugins/junit5/util/TestedAgp.kt | 10 +- .../projects/BuildScriptTemplateProcessor.kt | 81 ++--- .../BuildScriptTemplateProcessorTests.kt | 278 +++++++++--------- .../projects/FunctionalTestProjectCreator.kt | 63 ++-- .../util/projects/PluginSpecProjectCreator.kt | 32 +- .../junit5/util/projects/SemanticVersion.kt | 86 +++--- .../util/projects/SemanticVersionTests.kt | 135 +++++---- plugin/build.gradle.kts | 8 + 54 files changed, 1493 insertions(+), 1500 deletions(-) diff --git a/build-logic/gradle/libs.versions.toml b/build-logic/gradle/libs.versions.toml index d529165f..59c8320c 100644 --- a/build-logic/gradle/libs.versions.toml +++ b/build-logic/gradle/libs.versions.toml @@ -19,6 +19,7 @@ korte = "2.4.12" kotlin = "2.3.0" kotlinBinaryCompValidator = "0.17.0" kotlinCoroutines = "1.10.2" +ktfmt = "0.25.0" mockitoCore = "5.16.0" mockitoKotlin = "5.4.0" nexusPublish = "2.0.0" @@ -37,6 +38,7 @@ dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } kotlin-binarycompvalidator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "kotlinBinaryCompValidator" } kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } +ktfmt = { id = "com.ncorti.ktfmt.gradle", version.ref = "ktfmt" } publish = { id = "io.github.gradle-nexus.publish-plugin", version.ref = "nexusPublish" } shadow = { id = "com.github.johnrengelman.shadow", version.ref = "shadow" } diff --git a/plugin/android-junit5/build.gradle.kts b/plugin/android-junit5/build.gradle.kts index 9b4148ee..7af46d2b 100644 --- a/plugin/android-junit5/build.gradle.kts +++ b/plugin/android-junit5/build.gradle.kts @@ -1,18 +1,18 @@ import extensions.getWithVersion +import java.util.Locale import org.apache.tools.ant.filters.ReplaceTokens import org.gradle.api.attributes.Usage.JAVA_RUNTIME import org.gradle.api.attributes.Usage.USAGE_ATTRIBUTE import org.gradle.api.attributes.java.TargetJvmEnvironment.STANDARD_JVM import org.gradle.api.attributes.java.TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE import org.gradle.api.attributes.plugin.GradlePluginApiVersion.GRADLE_PLUGIN_API_VERSION_ATTRIBUTE -import java.util.Locale plugins { id("groovy") id("kotlin") id("java-gradle-plugin") id("jacoco") -// id("com.github.johnrengelman.shadow") + // id("com.github.johnrengelman.shadow") } val minimumGradleVersion = "8.2" @@ -28,9 +28,7 @@ val minimumGradleVersion = "8.2" // ------------------------------------------------------------------------------------------------ project.fixCompileTaskChain() -kotlin { - explicitApi() -} +kotlin { explicitApi() } // ------------------------------------------------------------------------------------------------ // Plugin Resource Setup @@ -55,9 +53,8 @@ dependencies { // Compile our plugin with the oldest supported AGP version // and its tools library, whose version is derived from it val agpVersion = SupportedAgp.oldest.version - val toolsVersion = agpVersion.split('.').run { - "${23 + first().toInt()}." + drop(1).joinToString(".") - } + val toolsVersion = + agpVersion.split('.').run { "${23 + first().toInt()}." + drop(1).joinToString(".") } compileOnly(libs.agp.getWithVersion(agpVersion)) compileOnly(libs.android.tools.getWithVersion(toolsVersion)) @@ -82,75 +79,74 @@ dependencies { // ------------------------------------------------------------------------------------------------ // Allow building fat JARs if necessary -//tasks.withType { +// tasks.withType { // isZip64 = true // enabled = project.hasProperty("enableFatJar") // archiveAppendix.set("fat") -//} +// } // Generate a file with the latest versions of the plugin & instrumentation val genFolder = "build/generated/sources/plugin" -val versionClassTask = tasks.register( - "createVersionClass", - Copy::configureCreateVersionClassTask, -) +val versionClassTask = + tasks.register("createVersionClass", Copy::configureCreateVersionClassTask) -sourceSets { - main { - kotlin.srcDir(genFolder) - } -} +sourceSets { main { kotlin.srcDir(genFolder) } } // Setup environment & versions for test projects // Create a test resource task which will power the instrumented tests // for different versions of the Android Gradle Plugin tasks.named("processTestResources").configure { - val tokens = mapOf( - "COMPILE_SDK_VERSION" to Android.compileSdkVersion.toString(), - "MIN_SDK_VERSION" to Android.sampleMinSdkVersion.toString(), - "TARGET_SDK_VERSION" to Android.targetSdkVersion.toString(), - - "KOTLIN_VERSION" to libs.versions.kotlin.get(), - "JUNIT5_ANDROID_LIBS_VERSION" to Artifacts.Instrumentation.Core.latestStableVersion, + val tokens = + mapOf( + "COMPILE_SDK_VERSION" to Android.compileSdkVersion.toString(), + "MIN_SDK_VERSION" to Android.sampleMinSdkVersion.toString(), + "TARGET_SDK_VERSION" to Android.targetSdkVersion.toString(), + "KOTLIN_VERSION" to libs.versions.kotlin.get(), + "JUNIT5_ANDROID_LIBS_VERSION" to Artifacts.Instrumentation.Core.latestStableVersion, - // Collect all supported AGP versions into a single string. - // This string is delimited with semicolons, and each of the separated values itself is a 4-tuple. - // - // Example: - // AGP_VERSIONS = 3.5|3.5.3|;3.6|3.6.3|6.4;3.7|3.7.0|8.0|33 - // - // Can be parsed into this list of values: - // |___> Short: "3.5" - // Full: "3.5.3" - // Gradle Requirement: "" - // Compile SDK: null - // - // |___> Short: "3.6" - // Full: "3.6.3" - // Gradle Requirement: "6.4" - // Compile SDK: null - // - // |___> Short: "3.7" - // Full: "3.7.0" - // Gradle Requirement: "8.0" - // Compile SDK: 33 - "AGP_VERSIONS" to SupportedAgp.values().joinToString(separator = ";") { plugin -> - "${plugin.shortVersion}|${plugin.version}|${plugin.gradle}|${plugin.compileSdk ?: ""}" - }, + // Collect all supported AGP versions into a single string. + // This string is delimited with semicolons, and each of the separated values itself is + // a 4-tuple. + // + // Example: + // AGP_VERSIONS = 3.5|3.5.3|;3.6|3.6.3|6.4;3.7|3.7.0|8.0|33 + // + // Can be parsed into this list of values: + // |___> Short: "3.5" + // Full: "3.5.3" + // Gradle Requirement: "" + // Compile SDK: null + // + // |___> Short: "3.6" + // Full: "3.6.3" + // Gradle Requirement: "6.4" + // Compile SDK: null + // + // |___> Short: "3.7" + // Full: "3.7.0" + // Gradle Requirement: "8.0" + // Compile SDK: 33 + "AGP_VERSIONS" to + SupportedAgp.values().joinToString(separator = ";") { plugin -> + "${plugin.shortVersion}|${plugin.version}|${plugin.gradle}|${plugin.compileSdk ?: ""}" + }, - // Collect all supported JUnit versions into a single string. - // This string is delimited with semicolons, and each of the separated values is a 2-tuple. - // Example: - // JUNIT_VERSIONS = 5|5.14.1;6|6.0.1 - "JUNIT_VERSIONS" to SupportedJUnit.values().joinToString(separator = ";") { junit -> - val fullVersion = when (junit) { - SupportedJUnit.JUnit5 -> libs.versions.junit5 - SupportedJUnit.JUnit6 -> libs.versions.junit6 - } - "${junit.majorVersion}|${fullVersion.get()}" - } - ) + // Collect all supported JUnit versions into a single string. + // This string is delimited with semicolons, and each of the separated values is a + // 2-tuple. + // Example: + // JUNIT_VERSIONS = 5|5.14.1;6|6.0.1 + "JUNIT_VERSIONS" to + SupportedJUnit.values().joinToString(separator = ";") { junit -> + val fullVersion = + when (junit) { + SupportedJUnit.JUnit5 -> libs.versions.junit5 + SupportedJUnit.JUnit6 -> libs.versions.junit6 + } + "${junit.majorVersion}|${fullVersion.get()}" + }, + ) inputs.properties(tokens) duplicatesStrategy = DuplicatesStrategy.INCLUDE @@ -167,34 +163,39 @@ tasks.named("processTestResources").configure { project.configurations.apply { SupportedAgp.values().forEach { plugin -> create(plugin.configurationName) { - description = "Local dependencies used for compiling & running " + + description = + "Local dependencies used for compiling & running " + "tests source code in Gradle functional tests against AGP ${plugin.version}" extendsFrom(configurations.getByName("implementation")) project.dependencies.add( /* configurationName = */ this.name, - /* dependencyNotation = */libs.agp.getWithVersion(plugin.version) + /* dependencyNotation = */ libs.agp.getWithVersion(plugin.version), ) - // For Android Gradle Plugins before 9.x, add the Kotlin Gradle Plugin explicitly, // acknowledging the different plugin variants introduced in Kotlin 1.7. - // Acknowledging the minimum required Gradle version, request the correct variant for KGP - // (see https://docs.gradle.org/current/userguide/implementing_gradle_plugins.html#plugin-with-variants) + // Acknowledging the minimum required Gradle version, request the correct variant for + // KGP + // (see + // https://docs.gradle.org/current/userguide/implementing_gradle_plugins.html#plugin-with-variants) if (plugin < SupportedAgp.AGP_9_0) { project.dependencies.add(this.name, libs.kgp.get()).apply { with(this as ExternalModuleDependency) { attributes { attribute( TARGET_JVM_ENVIRONMENT_ATTRIBUTE, - objects.named(TargetJvmEnvironment::class.java, STANDARD_JVM) + objects.named(TargetJvmEnvironment::class.java, STANDARD_JVM), ) attribute( USAGE_ATTRIBUTE, - objects.named(Usage::class.java, JAVA_RUNTIME) + objects.named(Usage::class.java, JAVA_RUNTIME), ) attribute( GRADLE_PLUGIN_API_VERSION_ATTRIBUTE, - objects.named(GradlePluginApiVersion::class.java, minimumGradleVersion) + objects.named( + GradlePluginApiVersion::class.java, + minimumGradleVersion, + ), ) } } @@ -217,40 +218,49 @@ project.configurations.apply { tasks.named("pluginUnderTestMetadata").configure { val defaultDirectory = outputs.files.singleFile - configurations.filter { it.name.startsWith("testAgp") }.forEach { configuration -> - val strippedName = configuration.name.substring(4).lowercase(Locale.ROOT) - val prunedFile = File(defaultDirectory, "pruned-plugin-metadata-$strippedName.properties") - outputs.file(prunedFile) + configurations + .filter { it.name.startsWith("testAgp") } + .forEach { configuration -> + val strippedName = configuration.name.substring(4).lowercase(Locale.ROOT) + val prunedFile = + File(defaultDirectory, "pruned-plugin-metadata-$strippedName.properties") + outputs.file(prunedFile) - doLast { - prunedFile.writer().use { writer -> - // 1) Use output classes from the plugin itself - // 2) Use resources from the plugin (i.e. plugin IDs etc.) - // 3) Use AGP-specific dependencies - val classesDirs = layout.buildDirectory.dir("classes").get().asFile.listFiles() - ?.filter { it.isDirectory } - ?.map { File(it, "main") } - ?.filter { it.exists() && it.isDirectory && it.list()?.isEmpty() == false } - ?: emptyList() - val resourcesDirs = layout.buildDirectory.dir("resources").get().asFile.listFiles() - ?.filter { it.isDirectory } - ?: emptyList() + doLast { + prunedFile.writer().use { writer -> + // 1) Use output classes from the plugin itself + // 2) Use resources from the plugin (i.e. plugin IDs etc.) + // 3) Use AGP-specific dependencies + val classesDirs = + layout.buildDirectory + .dir("classes") + .get() + .asFile + .listFiles() + ?.filter { it.isDirectory } + ?.map { File(it, "main") } + ?.filter { + it.exists() && it.isDirectory && it.list()?.isEmpty() == false + } ?: emptyList() + val resourcesDirs = + layout.buildDirectory.dir("resources").get().asFile.listFiles()?.filter { + it.isDirectory + } ?: emptyList() - writer.write("implementation-classpath=") - writer.write( - (classesDirs + resourcesDirs + configuration) - .joinToString(separator = "\\:") - ) + writer.write("implementation-classpath=") + writer.write( + (classesDirs + resourcesDirs + configuration).joinToString( + separator = "\\:" + ) + ) + } } } - } } project.configureDeployment(Artifacts.Plugin) // Register source-processing tasks as dependants of the custom source generation task listOf("compileKotlin", "sourcesJar", "dokkaGeneratePublicationHtml").forEach { taskName -> - tasks.named(taskName).configure { - dependsOn(versionClassTask) - } + tasks.named(taskName).configure { dependsOn(versionClassTask) } } diff --git a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/AndroidJUnitPlatformPlugin.kt b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/AndroidJUnitPlatformPlugin.kt index 1d6bde1e..fbac0ede 100644 --- a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/AndroidJUnitPlatformPlugin.kt +++ b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/AndroidJUnitPlatformPlugin.kt @@ -8,20 +8,21 @@ import org.gradle.api.Plugin import org.gradle.api.Project /** - * Android JUnit Platform plugin for Gradle. - * Configures JUnit 5 tasks on all unit-tested variants of an Android project. + * Android JUnit Platform plugin for Gradle. Configures JUnit 5 tasks on all unit-tested variants of + * an Android project. */ public class AndroidJUnitPlatformPlugin : Plugin { - override fun apply(project: Project): Unit = with(project) { - whenAndroidPluginAdded { plugin -> - PluginConfig.find(this, plugin)?.let { config -> - require(config.currentAgpVersion >= MIN_REQUIRED_AGP_VERSION) { - "android-junit5 plugin requires Android Gradle Plugin $MIN_REQUIRED_AGP_VERSION or later" - } + override fun apply(project: Project): Unit = + with(project) { + whenAndroidPluginAdded { plugin -> + PluginConfig.find(this, plugin)?.let { config -> + require(config.currentAgpVersion >= MIN_REQUIRED_AGP_VERSION) { + "android-junit5 plugin requires Android Gradle Plugin $MIN_REQUIRED_AGP_VERSION or later" + } - configureJUnitFramework(config) + configureJUnitFramework(config) + } } } - } } diff --git a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/dsl/AndroidJUnitPlatformExtension.kt b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/dsl/AndroidJUnitPlatformExtension.kt index a448609e..924f40ec 100644 --- a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/dsl/AndroidJUnitPlatformExtension.kt +++ b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/dsl/AndroidJUnitPlatformExtension.kt @@ -3,36 +3,30 @@ package de.mannodermaus.gradle.plugins.junit5.dsl import de.mannodermaus.Libraries import groovy.lang.Closure import groovy.lang.GroovyObjectSupport +import javax.inject.Inject import org.gradle.api.Action import org.gradle.api.Project import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.MapProperty import org.gradle.api.tasks.Input -import javax.inject.Inject -public abstract class AndroidJUnitPlatformExtension @Inject constructor( - project: Project, - private val objects: ObjectFactory -) : GroovyObjectSupport() { +public abstract class AndroidJUnitPlatformExtension +@Inject +constructor(project: Project, private val objects: ObjectFactory) : GroovyObjectSupport() { internal operator fun invoke(block: AndroidJUnitPlatformExtension.() -> Unit) { this.block() } - @get:Input - public abstract val configurationParameters: MapProperty + @get:Input public abstract val configurationParameters: MapProperty - /** - * Add a configuration parameter - */ + /** Add a configuration parameter */ public fun configurationParameter(key: String, value: String) { require(key.isNotBlank()) { "key must not be blank" } require('=' !in key) { "key must not contain '=': \"$key\"" } configurationParameters.put(key, value) } - /** - * Add a map of configuration parameters - */ + /** Add a map of configuration parameters */ public fun configurationParameters(parameters: Map) { parameters.forEach { configurationParameter(it.key, it.value) } } @@ -42,24 +36,19 @@ public abstract class AndroidJUnitPlatformExtension @Inject constructor( private val _filters = mutableMapOf() /** - * Configure the {@link FiltersExtension} - * for tests that belong to the provided build variant + * Configure the {@link FiltersExtension} for tests that belong to the provided build variant */ public fun filters(qualifier: String?, action: Action) { action.execute(filters(qualifier)) } - /** - * Configure the global {@link FiltersExtension} for all variants. - */ + /** Configure the global {@link FiltersExtension} for all variants. */ public fun filters(action: Action) { filters(null, action) } internal fun filters(qualifier: String? = null): FiltersExtension { - return _filters.getOrPut(qualifier) { - objects.newInstance(FiltersExtension::class.java) - } + return _filters.getOrPut(qualifier) { objects.newInstance(FiltersExtension::class.java) } } @Suppress("unused") @@ -81,9 +70,7 @@ public abstract class AndroidJUnitPlatformExtension @Inject constructor( /* Android Instrumentation Test support */ - /** - * Options for controlling instrumentation test execution with JUnit 5 - */ + /** Options for controlling instrumentation test execution with JUnit 5 */ public val instrumentationTests: InstrumentationTestOptions = objects.newInstance(InstrumentationTestOptions::class.java).apply { enabled.convention(true) @@ -98,9 +85,7 @@ public abstract class AndroidJUnitPlatformExtension @Inject constructor( /* Jacoco Reporting Integration */ - /** - * Options for controlling Jacoco reporting - */ + /** Options for controlling Jacoco reporting */ public val jacocoOptions: JacocoOptions = objects.newInstance(JacocoOptions::class.java).apply { taskGenerationEnabled.convention(true) diff --git a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/dsl/FiltersExtension.kt b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/dsl/FiltersExtension.kt index f6a69c9c..c8842df8 100644 --- a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/dsl/FiltersExtension.kt +++ b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/dsl/FiltersExtension.kt @@ -5,77 +5,56 @@ import de.mannodermaus.gradle.plugins.junit5.internal.utils.IncludeExcludeContai public abstract class FiltersExtension { /** - * Class name patterns in the form of regular expressions for - * classes that should be included in the test plan. + * Class name patterns in the form of regular expressions for classes that should be + * included in the test plan. * - *

The patterns are combined using OR semantics, i.e. if the fully - * qualified name of a class matches against at least one of the patterns, - * the class will be included in the test plan. + *

The patterns are combined using OR semantics, i.e. if the fully qualified name of a class + * matches against at least one of the patterns, the class will be included in the test plan. */ internal val patterns = IncludeExcludeContainer() - /** - * Add a pattern to the list of included patterns - */ + /** Add a pattern to the list of included patterns */ public fun includePattern(pattern: String) { includePatterns(pattern) } - /** - * Add patterns to the list of included patterns - */ + /** Add patterns to the list of included patterns */ public fun includePatterns(vararg patterns: String) { this.patterns.include(*patterns) } - /** - * Add a pattern to the list of excluded patterns - */ + /** Add a pattern to the list of excluded patterns */ public fun excludePattern(pattern: String) { excludePatterns(pattern) } - /** - * Add patterns to the list of excluded patterns - */ + /** Add patterns to the list of excluded patterns */ public fun excludePatterns(vararg patterns: String) { this.patterns.exclude(*patterns) } - /** - * Included & Excluded JUnit 5 tags. - */ + /** Included & Excluded JUnit 5 tags. */ internal val tags = IncludeExcludeContainer() - /** - * Add tags to the list of included tags - */ + /** Add tags to the list of included tags */ public fun includeTags(vararg tags: String) { this.tags.include(*tags) } - /** - * Add tags to the list of excluded tags - */ + /** Add tags to the list of excluded tags */ public fun excludeTags(vararg tags: String) { this.tags.exclude(*tags) } - /** - * Included & Excluded JUnit 5 engines. - */ + /** Included & Excluded JUnit 5 engines. */ internal val engines = IncludeExcludeContainer() - /** - * Add engines to the list of included engines - */ + /** Add engines to the list of included engines */ public fun includeEngines(vararg engines: String) { this.engines.include(*engines) } - /** - * Add engines to the list of excluded engines - */ + /** Add engines to the list of excluded engines */ public fun excludeEngines(vararg engines: String) { this.engines.exclude(*engines) } diff --git a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/dsl/InstrumentationTestOptions.kt b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/dsl/InstrumentationTestOptions.kt index b562ee16..fd56dc84 100644 --- a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/dsl/InstrumentationTestOptions.kt +++ b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/dsl/InstrumentationTestOptions.kt @@ -3,35 +3,27 @@ package de.mannodermaus.gradle.plugins.junit5.dsl import org.gradle.api.provider.Property import org.gradle.api.tasks.Input -/** - * Options for controlling instrumentation test execution - */ +/** Options for controlling instrumentation test execution */ public abstract class InstrumentationTestOptions { /** - * Whether to configure JUnit 5 instrumentation tests automatically - * when junit-jupiter-api is added as an androidTestImplementation dependency. + * Whether to configure JUnit 5 instrumentation tests automatically when junit-jupiter-api is + * added as an androidTestImplementation dependency. */ - @get:Input - public abstract val enabled: Property + @get:Input public abstract val enabled: Property - /** - * The version of the instrumentation libraries to autoconfigure. - */ - @get:Input - public abstract val version: Property + /** The version of the instrumentation libraries to autoconfigure. */ + @get:Input public abstract val version: Property /** - * Whether to include a dependency on the android-test-extensions library - * on top of the main instrumentation artifacts. + * Whether to include a dependency on the android-test-extensions library on top of the main + * instrumentation artifacts. */ - @get:Input - public abstract val includeExtensions: Property + @get:Input public abstract val includeExtensions: Property /** - * Whether to use configuration parameters configured via the plugin DSL - * for instrumentation tests, too. + * Whether to use configuration parameters configured via the plugin DSL for instrumentation + * tests, too. */ - @get:Input - public abstract val useConfigurationParameters: Property + @get:Input public abstract val useConfigurationParameters: Property } diff --git a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/dsl/JacocoOptions.kt b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/dsl/JacocoOptions.kt index d0287228..0723eea3 100644 --- a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/dsl/JacocoOptions.kt +++ b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/dsl/JacocoOptions.kt @@ -1,5 +1,6 @@ package de.mannodermaus.gradle.plugins.junit5.dsl +import javax.inject.Inject import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.FileSystemLocation import org.gradle.api.file.FileSystemLocationProperty @@ -8,74 +9,50 @@ import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property import org.gradle.api.provider.SetProperty -import org.gradle.api.reporting.Report.OutputType import org.gradle.api.tasks.Input -import org.gradle.internal.enterprise.test.FileProperty -import javax.inject.Inject -/** - * Options for controlling Jacoco reporting - */ -public abstract class JacocoOptions @Inject constructor( - objects: ObjectFactory -) { +/** Options for controlling Jacoco reporting */ +public abstract class JacocoOptions @Inject constructor(objects: ObjectFactory) { - /** - * Whether to enable Jacoco task integration - */ - @get:Input - public abstract val taskGenerationEnabled: Property + /** Whether to enable Jacoco task integration */ + @get:Input public abstract val taskGenerationEnabled: Property /** - * Filter the generated Jacoco tasks, - * so that only the given build variants are provided with a companion task. - * Make sure to add the full product flavor name if necessary - * (i.e. "paidDebug" if you use a "paid" product flavor and the "debug" build type). - * By default, this set is empty, meaning that tasks are generated for all variants. + * Filter the generated Jacoco tasks, so that only the given build variants are provided with a + * companion task. Make sure to add the full product flavor name if necessary (i.e. "paidDebug" + * if you use a "paid" product flavor and the "debug" build type). By default, this set is + * empty, meaning that tasks are generated for all variants. */ - @get:Input - public abstract val onlyGenerateTasksForVariants: SetProperty + @get:Input public abstract val onlyGenerateTasksForVariants: SetProperty - /** - * Options for controlling the HTML Report generated by Jacoco - */ + /** Options for controlling the HTML Report generated by Jacoco */ public val html: DirectoryReport = objects.newInstance(DirectoryReport::class.java) - /** - * Options for controlling the CSV Report generated by Jacoco - */ + /** Options for controlling the CSV Report generated by Jacoco */ public val csv: FileReport = objects.newInstance(FileReport::class.java) - /** - * Options for controlling the XML Report generated by Jacoco - */ + /** Options for controlling the XML Report generated by Jacoco */ public val xml: FileReport = objects.newInstance(FileReport::class.java) /** - * List of class name patterns that should be excluded from being processed by Jacoco. - * By default, this will exclude R.class & BuildConfig.class + * List of class name patterns that should be excluded from being processed by Jacoco. By + * default, this will exclude R.class & BuildConfig.class */ - @get:Input - public abstract val excludedClasses: ListProperty + @get:Input public abstract val excludedClasses: ListProperty public sealed class Report { - /** - * Whether this report should be generated - */ - @get:Input - public abstract val enabled: Property + /** Whether this report should be generated */ + @get:Input public abstract val enabled: Property /** - * Name of the file/directory to be generated; note that - * due to the variant-aware nature of the plugin, - * each variant will be assigned a distinct folder if necessary + * Name of the file/directory to be generated; note that due to the variant-aware nature of + * the plugin, each variant will be assigned a distinct folder if necessary */ public abstract val destination: FileSystemLocationProperty } public abstract class DirectoryReport : Report() { - @get:Input - public abstract override val destination: DirectoryProperty + @get:Input public abstract override val destination: DirectoryProperty public operator fun invoke(config: DirectoryReport.() -> Unit) { this.config() @@ -83,8 +60,7 @@ public abstract class JacocoOptions @Inject constructor( } public abstract class FileReport : Report() { - @get:Input - public abstract override val destination: RegularFileProperty + @get:Input public abstract override val destination: RegularFileProperty public operator fun invoke(config: FileReport.() -> Unit) { this.config() diff --git a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/config/Constants.kt b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/config/Constants.kt index fa5701db..fbfa7e23 100644 --- a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/config/Constants.kt +++ b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/config/Constants.kt @@ -3,14 +3,14 @@ package de.mannodermaus.gradle.plugins.junit5.internal.config import com.android.build.api.AndroidPluginVersion -import org.gradle.util.GradleVersion // When updating this, check buildSrc/Tasks.kt and update it there, too internal val MIN_REQUIRED_AGP_VERSION = AndroidPluginVersion(8, 2) internal const val EXTENSION_NAME = "junitPlatform" -internal const val ANDROID_JUNIT5_RUNNER_BUILDER_CLASS = "de.mannodermaus.junit5.AndroidJUnit5Builder" +internal const val ANDROID_JUNIT5_RUNNER_BUILDER_CLASS = + "de.mannodermaus.junit5.AndroidJUnit5Builder" internal const val INSTRUMENTATION_RUNNER_LIBRARY_GROUP = "de.mannodermaus.junit5" internal const val INSTRUMENTATION_RUNNER_LIBRARY_ARTIFACT = "android-test-runner" diff --git a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/config/JUnitPlatformTaskConfig.kt b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/config/JUnitPlatformTaskConfig.kt index b5579471..fbb43b3f 100644 --- a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/config/JUnitPlatformTaskConfig.kt +++ b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/config/JUnitPlatformTaskConfig.kt @@ -12,29 +12,31 @@ internal class JUnitPlatformTaskConfig( private val extension: AndroidJUnitPlatformExtension, ) { - // There is a distinct application order, which determines how values are merged and overwritten. + // There is a distinct application order, which determines how values are merged and + // overwritten. // From top to bottom, this list goes as follows (values on the bottom will override conflicting // entries specified above them): // 1) Default ("filters") // 2) Build-type-specific (e.g. "debug") // 3) Flavor-specific (e.g. "free") // 4) Variant-specific (e.g. "freeDebug") - private fun collect(func: FiltersExtension.() -> IncludeExcludeContainer): IncludeExcludeContainer { + private fun collect( + func: FiltersExtension.() -> IncludeExcludeContainer + ): IncludeExcludeContainer { // 1) val layer1 = filtersOf(null, func) // 2) val layer2 = layer1 + filtersOf(variant.buildType, func) // 3) - val layer3 = variant.productFlavors - .map { filtersOf(it.second, func) } - .fold(layer2) { a, b -> a + b } + val layer3 = + variant.productFlavors.map { filtersOf(it.second, func) }.fold(layer2) { a, b -> a + b } // 4) return layer3 + filtersOf(variant.name, func) } private inline fun filtersOf( qualifier: String?, - func: FiltersExtension.() -> IncludeExcludeContainer + func: FiltersExtension.() -> IncludeExcludeContainer, ): IncludeExcludeContainer = extension.filters(qualifier).run { func() } val combinedIncludePatterns = this.collect { patterns }.include.toTypedArray() diff --git a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/config/PluginConfig.kt b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/config/PluginConfig.kt index 80461da3..6764bbb5 100644 --- a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/config/PluginConfig.kt +++ b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/config/PluginConfig.kt @@ -21,23 +21,27 @@ internal class PluginConfig private constructor( private val project: Project, private val legacyPlugin: BasePlugin, - private val componentsExtension: AndroidComponentsExtension<*, *, *> + private val componentsExtension: AndroidComponentsExtension<*, *, *>, ) { companion object { fun find(project: Project, plugin: BasePlugin): PluginConfig? { - val componentsExtension = project.extensions - .findByName("androidComponents") as? AndroidComponentsExtension<*, *, *> - ?: return null + val componentsExtension = + project.extensions.findByName("androidComponents") + as? AndroidComponentsExtension<*, *, *> ?: return null return PluginConfig(project, plugin, componentsExtension) } } - val hasJacocoPlugin get() = project.plugins.hasPlugin("jacoco") - private val hasKotlinPlugin get() = project.plugins.findPlugin("kotlin-android") != null + val hasJacocoPlugin + get() = project.plugins.hasPlugin("jacoco") - val currentAgpVersion get() = componentsExtension.pluginVersion + private val hasKotlinPlugin + get() = project.plugins.findPlugin("kotlin-android") != null + + val currentAgpVersion + get() = componentsExtension.pluginVersion fun finalizeDsl(block: (CommonExtension<*, *, *, *, *>) -> Unit) { componentsExtension.finalizeDsl(block) @@ -52,19 +56,21 @@ private constructor( // does not give access to variant-specific source sets and class outputs val legacyExtension = project.extensions.findByName("android") - val legacyVariants = try { - when (legacyPlugin) { - is AppPlugin -> (legacyExtension as AppExtension).applicationVariants - is LibraryPlugin -> (legacyExtension as LibraryExtension).libraryVariants - is DynamicFeaturePlugin -> (legacyExtension as AppExtension).applicationVariants - else -> null + val legacyVariants = + try { + when (legacyPlugin) { + is AppPlugin -> (legacyExtension as AppExtension).applicationVariants + is LibraryPlugin -> (legacyExtension as LibraryExtension).libraryVariants + is DynamicFeaturePlugin -> (legacyExtension as AppExtension).applicationVariants + else -> null + } + } catch (_: ClassCastException) { + // AGP 9 removes access to the legacy API and thus, Jacoco integration + // is deprecated henceforth. When the above block yields a ClassCastException, + // we know that we're using exclusively against the new DSL and return an empty set + // to the caller + null } - } catch (_: ClassCastException) { - // AGP 9 removes access to the legacy API and thus, Jacoco integration - // is deprecated henceforth. When the above block yields a ClassCastException, - // we know that we're using exclusively against the new DSL and return an empty set to the caller - null - } return legacyVariants ?.firstOrNull { it.name == variant.name } diff --git a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/configure.kt b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/configure.kt index 985c79df..ec9ffefc 100644 --- a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/configure.kt +++ b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/configure.kt @@ -18,7 +18,6 @@ import de.mannodermaus.gradle.plugins.junit5.internal.extensions.getTaskName import de.mannodermaus.gradle.plugins.junit5.internal.extensions.instrumentationTestVariant import de.mannodermaus.gradle.plugins.junit5.internal.extensions.junit5Warn import de.mannodermaus.gradle.plugins.junit5.internal.extensions.namedOrNull -import de.mannodermaus.gradle.plugins.junit5.internal.extensions.usesComposeIn import de.mannodermaus.gradle.plugins.junit5.internal.extensions.usesJUnitJupiterIn import de.mannodermaus.gradle.plugins.junit5.internal.usage.DependencyUsageDetector import de.mannodermaus.gradle.plugins.junit5.tasks.AndroidJUnit5JacocoReport @@ -92,19 +91,22 @@ private fun AndroidJUnitPlatformExtension.prepareVariantDsl(variant: Variant) { } } -private fun AndroidJUnitPlatformExtension.prepareUnitTests(project: Project, android: AndroidExtension) { +private fun AndroidJUnitPlatformExtension.prepareUnitTests( + project: Project, + android: AndroidExtension, +) { // Add default ignore rules for JUnit 5 metadata files to the packaging options of the plugin, // so that consumers don't need to do this explicitly android.packaging.resources.excludes.addAll( - listOf( - "/META-INF/LICENSE.md", - "/META-INF/LICENSE-notice.md" - ) + listOf("/META-INF/LICENSE.md", "/META-INF/LICENSE-notice.md") ) attachDependencies(project, "testImplementation") } -private fun AndroidJUnitPlatformExtension.prepareInstrumentationTests(project: Project, android: AndroidExtension) { +private fun AndroidJUnitPlatformExtension.prepareInstrumentationTests( + project: Project, + android: AndroidExtension, +) { // Automatically configure instrumentation tests when JUnit 5 is detected in that configuration if (!instrumentationTests.enabled.get()) return if (!project.usesJUnitJupiterIn("androidTestImplementation")) return @@ -114,10 +116,11 @@ private fun AndroidJUnitPlatformExtension.prepareInstrumentationTests(project: P // Attach the JUnit 5 RunnerBuilder to the list, unless it's already added val runnerBuilders = runnerArgs.getAsList("runnerBuilder") if (ANDROID_JUNIT5_RUNNER_BUILDER_CLASS !in runnerBuilders) { - runnerArgs["runnerBuilder"] = runnerBuilders - .toMutableList() - .also { it.add(ANDROID_JUNIT5_RUNNER_BUILDER_CLASS) } - .joinToString(",") + runnerArgs["runnerBuilder"] = + runnerBuilders + .toMutableList() + .also { it.add(ANDROID_JUNIT5_RUNNER_BUILDER_CLASS) } + .joinToString(",") } // Copy over configuration parameters to instrumentation tests @@ -135,8 +138,9 @@ private fun AndroidJUnitPlatformExtension.prepareInstrumentationTests(project: P } /** - * Construct an artifact ID (i.e. `de.mannodermaus:hoge:1.2.3`) compatible with the given supported JUnit version. - * Depending on the JUnit version, the artifact ID may include a version-specific suffix string as well. + * Construct an artifact ID (i.e. `de.mannodermaus:hoge:1.2.3`) compatible with the given supported + * JUnit version. Depending on the JUnit version, the artifact ID may include a version-specific + * suffix string as well. */ internal fun Libraries.JUnit.artifact(base: String, version: String?) = buildString { append(base) @@ -147,7 +151,10 @@ internal fun Libraries.JUnit.artifact(base: String, version: String?) = buildStr } } -private fun AndroidJUnitPlatformExtension.attachDependencies(project: Project, configurationName: String) { +private fun AndroidJUnitPlatformExtension.attachDependencies( + project: Project, + configurationName: String, +) { val detector = DependencyUsageDetector(project) detector.isUsingJUnit(configurationName)?.let { usage -> @@ -156,29 +163,38 @@ private fun AndroidJUnitPlatformExtension.attachDependencies(project: Project, c val version = instrumentationTests.version.get() // First, apply the core library - project.dependencies.add(configurationName, usage.junit.artifact(Instrumentation.core, version)) + project.dependencies.add( + configurationName, + usage.junit.artifact(Instrumentation.core, version), + ) // Add some runtime dependencies, including a reference to the JUnit BOM project.dependencies.add( runtimeOnly, - project.dependencies.platform("org.junit:junit-bom:${usage.junit.fullVersion}") + project.dependencies.platform("org.junit:junit-bom:${usage.junit.fullVersion}"), ) project.dependencies.add(runtimeOnly, Libraries.junitPlatformLauncher) if (includeRunner) { project.dependencies.add( runtimeOnly, - usage.junit.artifact(Instrumentation.runner, version) + usage.junit.artifact(Instrumentation.runner, version), ) } // Add optional artifacts if (instrumentationTests.includeExtensions.get()) { - project.dependencies.add(configurationName, usage.junit.artifact(Instrumentation.extensions, version)) + project.dependencies.add( + configurationName, + usage.junit.artifact(Instrumentation.extensions, version), + ) } if (detector.isUsingCompose(configurationName)) { - project.dependencies.add(configurationName, usage.junit.artifact(Instrumentation.compose, version)) + project.dependencies.add( + configurationName, + usage.junit.artifact(Instrumentation.compose, version), + ) } } } @@ -201,7 +217,8 @@ private fun AndroidJUnitPlatformExtension.configureUnitTests(project: Project, v // From the User Guide: // "The standard Gradle test task currently does not provide a dedicated DSL // to set JUnit Platform configuration parameters to influence test discovery and execution. - // However, you can provide configuration parameters within the build script via system properties" + // However, you can provide configuration parameters within the build script via system + // properties" testTask.systemProperties(configurationParameters.get()) } } @@ -209,7 +226,7 @@ private fun AndroidJUnitPlatformExtension.configureUnitTests(project: Project, v private fun AndroidJUnitPlatformExtension.configureJacoco( project: Project, config: PluginConfig, - variant: Variant + variant: Variant, ) { // Connect a Code Coverage report to it if Jacoco is enabled if (jacocoOptions.taskGenerationEnabled.get() && config.hasJacocoPlugin) { @@ -222,19 +239,20 @@ private fun AndroidJUnitPlatformExtension.configureJacoco( // the unavailability of Jacoco integration on certain AGP versions // (namely, AGP 9.0.0+ with the new DSL). This feature is effectively deprecated val directoryProviders = config.directoryProvidersOf(variant) - val registeredTask = AndroidJUnit5JacocoReport.register( - project = project, - variant = variant, - testTask = testTask, - directoryProviders = directoryProviders - ) + val registeredTask = + AndroidJUnit5JacocoReport.register( + project = project, + variant = variant, + testTask = testTask, + directoryProviders = directoryProviders, + ) if (directoryProviders.isNotEmpty()) { // Log a warning if Jacoco tasks already existed if (registeredTask == null) { project.logger.junit5Warn( "Jacoco task for variant '${variant.name}' already exists." + - "Disabling customization for JUnit 5..." + "Disabling customization for JUnit 5..." ) } } else { @@ -251,12 +269,8 @@ private fun AndroidJUnitPlatformExtension.configureJacoco( append( " This integration is deprecated from AGP 9.0.0 onwards because of the new DSL." ) - append( - " Please consult the link below for more information: " - ) - append( - "https://developer.android.com/build/releases/agp-preview" - ) + append(" Please consult the link below for more information: ") + append("https://developer.android.com/build/releases/agp-preview") } } ) diff --git a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/extensions/ConfigurableReportExt.kt b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/extensions/ConfigurableReportExt.kt index 4e9bbbc8..c6e7972e 100644 --- a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/extensions/ConfigurableReportExt.kt +++ b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/extensions/ConfigurableReportExt.kt @@ -1,18 +1,21 @@ package de.mannodermaus.gradle.plugins.junit5.internal.extensions +import java.lang.reflect.Method import org.gradle.api.file.FileSystemLocationProperty import org.gradle.api.reporting.ConfigurableReport -import java.lang.reflect.Method internal val ConfigurableReport.outputLocationFile: FileSystemLocationProperty<*> - get() = try { - outputLocation as FileSystemLocationProperty<*> - } catch (e: NoSuchMethodError) { - // Observed before Gradle 8.x - getOutputLocationMethod.invoke(this) as FileSystemLocationProperty<*> - } + get() = + try { + outputLocation as FileSystemLocationProperty<*> + } catch (e: NoSuchMethodError) { + // Observed before Gradle 8.x + getOutputLocationMethod.invoke(this) as FileSystemLocationProperty<*> + } private val ConfigurableReport.getOutputLocationMethod: Method - get() = javaClass.declaredMethods.first { method -> - method.name == "getOutputLocation" && method.returnType == FileSystemLocationProperty::class.java - } + get() = + javaClass.declaredMethods.first { method -> + method.name == "getOutputLocation" && + method.returnType == FileSystemLocationProperty::class.java + } diff --git a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/extensions/LoggerExt.kt b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/extensions/LoggerExt.kt index 6a41c713..2752e959 100644 --- a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/extensions/LoggerExt.kt +++ b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/extensions/LoggerExt.kt @@ -4,12 +4,13 @@ import org.gradle.api.logging.LogLevel import org.gradle.api.logging.Logger internal fun Logger.agpLog(level: LogLevel, message: String) { - val pair: Pair Unit> = when (level) { - LogLevel.ERROR -> "error" to { s -> error(s) } - LogLevel.WARN -> "warning" to { s -> warn(s) } - LogLevel.INFO -> "info" to { s -> info(s) } - else -> "debug" to { s -> debug(s) } - } + val pair: Pair Unit> = + when (level) { + LogLevel.ERROR -> "error" to { s -> error(s) } + LogLevel.WARN -> "warning" to { s -> warn(s) } + LogLevel.INFO -> "info" to { s -> info(s) } + else -> "debug" to { s -> debug(s) } + } val (kind, log) = pair log("""AGBPI: {"kind": "$kind","text":"$message"}""") } diff --git a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/extensions/MapExt.kt b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/extensions/MapExt.kt index 8a046a58..1a3e0bc9 100644 --- a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/extensions/MapExt.kt +++ b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/extensions/MapExt.kt @@ -1,4 +1,4 @@ package de.mannodermaus.gradle.plugins.junit5.internal.extensions internal fun Map.getAsList(key: String, delimiter: String = ","): List = - this[key]?.split(delimiter) ?: emptyList() + this[key]?.split(delimiter) ?: emptyList() diff --git a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/extensions/ProjectExt.kt b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/extensions/ProjectExt.kt index e20f4d88..ef089743 100644 --- a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/extensions/ProjectExt.kt +++ b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/extensions/ProjectExt.kt @@ -3,10 +3,10 @@ package de.mannodermaus.gradle.plugins.junit5.internal.extensions import com.android.build.gradle.BasePlugin import de.mannodermaus.gradle.plugins.junit5.dsl.AndroidJUnitPlatformExtension import de.mannodermaus.gradle.plugins.junit5.internal.config.EXTENSION_NAME -import org.gradle.api.Project -import org.gradle.api.artifacts.Dependency import java.util.concurrent.atomic.AtomicBoolean import kotlin.contracts.ExperimentalContracts +import org.gradle.api.Project +import org.gradle.api.artifacts.Dependency internal val Project.junitPlatform get() = extensionByName(EXTENSION_NAME) @@ -24,12 +24,17 @@ internal fun Project.whenAndroidPluginAdded(block: (BasePlugin) -> Unit) { afterEvaluate { // If no Android plugin was applied by this point, fail if (!configured.get()) { - throw IllegalStateException("An Android plugin must be applied in order for android-junit5 to work correctly!") + throw IllegalStateException( + "An Android plugin must be applied in order for android-junit5 to work correctly!" + ) } } } -internal fun Project.hasDependency(configurationName: String, matching: (Dependency) -> Boolean): Boolean { +internal fun Project.hasDependency( + configurationName: String, + matching: (Dependency) -> Boolean, +): Boolean { val configuration = project.configurations.getByName(configurationName) return configuration.dependencies.any(matching) diff --git a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/extensions/StringExt.kt b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/extensions/StringExt.kt index 68d72fc9..c2764c78 100644 --- a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/extensions/StringExt.kt +++ b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/extensions/StringExt.kt @@ -2,9 +2,7 @@ package de.mannodermaus.gradle.plugins.junit5.internal.extensions import java.util.* -/** - * Replacement for Kotlin's deprecated [capitalize] method on strings - */ +/** Replacement for Kotlin's deprecated [capitalize] method on strings */ internal fun String.capitalized() = replaceFirstChar { if (it.isLowerCase()) { it.titlecase(Locale.getDefault()) diff --git a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/extensions/VariantExt.kt b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/extensions/VariantExt.kt index 9b226f4e..66558f93 100644 --- a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/extensions/VariantExt.kt +++ b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/extensions/VariantExt.kt @@ -8,17 +8,19 @@ internal fun Variant.getTaskName(prefix: String = "", suffix: String = ""): Stri // At least one value must be provided require(prefix.isNotEmpty() || suffix.isNotEmpty()) - return StringBuilder().apply { - append(prefix) - append( - if (isEmpty()) { - name - } else { - name.capitalized() - } - ) - append(suffix.capitalized()) - }.toString() + return StringBuilder() + .apply { + append(prefix) + append( + if (isEmpty()) { + name + } else { + name.capitalized() + } + ) + append(suffix.capitalized()) + } + .toString() } internal val Variant.instrumentationTestVariant: AndroidTest? diff --git a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/providers/DirectoryProvider.kt b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/providers/DirectoryProvider.kt index 53b8660c..5d1c68a3 100644 --- a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/providers/DirectoryProvider.kt +++ b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/providers/DirectoryProvider.kt @@ -3,38 +3,32 @@ package de.mannodermaus.gradle.plugins.junit5.internal.providers import java.io.File /** - * General interface for providers of class & source directories - * towards the construction of JUnit 5 tasks and its companions. + * General interface for providers of class & source directories towards the construction of JUnit 5 + * tasks and its companions. * - * Registered through the plugin, integrations with different languages - * and frameworks can provide their own collection of directories. - * The most prominent example consists of the opt-in Kotlin support, - * which provides the "/kotlin" directories to each JUnit 5 task, - * allowing Kotlin classes to be used for test detection & execution. + * Registered through the plugin, integrations with different languages and frameworks can provide + * their own collection of directories. The most prominent example consists of the opt-in Kotlin + * support, which provides the "/kotlin" directories to each JUnit 5 task, allowing Kotlin classes + * to be used for test detection & execution. */ internal interface DirectoryProvider { - /** - * The locations of compiled class files - */ + /** The locations of compiled class files */ fun mainClassDirectories(): Set - /** - * The locations of compiled test class files - */ + /** The locations of compiled test class files */ fun testClassDirectories(): Set - /** - * The locations of source files - */ + /** The locations of source files */ fun mainSourceDirectories(): Set - /** - * The locations of test source files - */ + /** The locations of test source files */ fun testSourceDirectories(): Set } /* Extensions */ -internal fun Iterable.mainClassDirectories() = flatMap { it.mainClassDirectories() }.distinct() -internal fun Iterable.mainSourceDirectories() = flatMap { it.mainSourceDirectories() }.distinct() +internal fun Iterable.mainClassDirectories() = + flatMap { it.mainClassDirectories() }.distinct() + +internal fun Iterable.mainSourceDirectories() = + flatMap { it.mainSourceDirectories() }.distinct() diff --git a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/providers/JavaDirectoryProvider.kt b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/providers/JavaDirectoryProvider.kt index f52b53c3..a7729e3a 100644 --- a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/providers/JavaDirectoryProvider.kt +++ b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/providers/JavaDirectoryProvider.kt @@ -6,24 +6,26 @@ import com.android.build.gradle.api.BaseVariant import de.mannodermaus.gradle.plugins.junit5.internal.extensions.unitTestVariant /** - * Default Provider implementation for Java-based test root directories. - * This will look up the main & test root directories - * of the variant connected to a given JUnit 5 task. + * Default Provider implementation for Java-based test root directories. This will look up the main + * & test root directories of the variant connected to a given JUnit 5 task. */ internal class JavaDirectoryProvider(private val variant: BaseVariant) : DirectoryProvider { override fun mainSourceDirectories() = sourceFoldersOf(variant) + override fun testSourceDirectories() = sourceFoldersOf(variant.unitTestVariant) + override fun mainClassDirectories() = classFoldersOf(variant) + override fun testClassDirectories() = classFoldersOf(variant.unitTestVariant) /* Private */ private fun sourceFoldersOf(variant: BaseVariant) = - variant.sourceSets - .flatMap { it.javaDirectories } - .toSet() + variant.sourceSets.flatMap { it.javaDirectories }.toSet() private fun classFoldersOf(variant: BaseVariant) = - setOfNotNull(variant.javaCompileProvider.map { it.destinationDirectory.asFile }.get().orNull) + setOfNotNull( + variant.javaCompileProvider.map { it.destinationDirectory.asFile }.get().orNull + ) } diff --git a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/providers/KotlinDirectoryProvider.kt b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/providers/KotlinDirectoryProvider.kt index 7699fd69..4431b848 100644 --- a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/providers/KotlinDirectoryProvider.kt +++ b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/providers/KotlinDirectoryProvider.kt @@ -5,33 +5,31 @@ package de.mannodermaus.gradle.plugins.junit5.internal.providers import com.android.build.gradle.api.BaseVariant import de.mannodermaus.gradle.plugins.junit5.internal.extensions.agpLog import de.mannodermaus.gradle.plugins.junit5.internal.extensions.unitTestVariant +import java.io.File import org.gradle.api.Project import org.gradle.api.logging.LogLevel.WARN import org.jetbrains.kotlin.gradle.tasks.KotlinCompile -import java.io.File /* Types */ -/** - * Provides test root directories for Kotlin sources, - * with which a JUnit 5 Task can be enhanced. - */ +/** Provides test root directories for Kotlin sources, with which a JUnit 5 Task can be enhanced. */ internal class KotlinDirectoryProvider( private val project: Project, - private val variant: BaseVariant + private val variant: BaseVariant, ) : DirectoryProvider { override fun mainSourceDirectories() = sourceFoldersOf(variant) + override fun testSourceDirectories() = sourceFoldersOf(variant.unitTestVariant) + override fun mainClassDirectories() = classFoldersOf(variant) + override fun testClassDirectories() = classFoldersOf(variant.unitTestVariant) /* Private */ private fun sourceFoldersOf(variant: BaseVariant) = - variant.sourceSets - .flatMap { it.javaDirectories + it.kotlinDirectories } - .toSet() + variant.sourceSets.flatMap { it.javaDirectories + it.kotlinDirectories }.toSet() private fun classFoldersOf(variant: BaseVariant): Set { val kotlinTask = project.tasks.findByName(variant.kotlinTaskName) @@ -43,7 +41,7 @@ internal class KotlinDirectoryProvider( // fall back to the expected path… However, make sure to log a warning to users! project.logger.agpLog( WARN, - "The kotlin-android plugin is currently applied after android-junit5! To guarantee full compatibility, please declare it above the JUnit 5 plugin." + "The kotlin-android plugin is currently applied after android-junit5! To guarantee full compatibility, please declare it above the JUnit 5 plugin.", ) setOf(File(project.buildDir, "tmp/kotlin-classes/${variant.name}")) } diff --git a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/usage/DependencyUsageDetector.kt b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/usage/DependencyUsageDetector.kt index 67e040ab..61462ff3 100644 --- a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/usage/DependencyUsageDetector.kt +++ b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/usage/DependencyUsageDetector.kt @@ -4,41 +4,39 @@ import de.mannodermaus.Libraries.JUnit import org.gradle.api.Project import org.gradle.api.artifacts.Dependency -/** - * Helper class to locate the usage of a particular library version in the project. - */ +/** Helper class to locate the usage of a particular library version in the project. */ internal class DependencyUsageDetector(private val project: Project) { data class JUnitUsage(val junit: JUnit) fun isUsingJUnit(configurationName: String): JUnitUsage? { - val match = find( - configurationName, - { it.group == "org.junit" && it.name == "junit-bom" }, - { it.group == "org.junit.jupiter" && it.name == "junit-jupiter-api" }, - ) ?: return null + val match = + find( + configurationName, + { it.group == "org.junit" && it.name == "junit-bom" }, + { it.group == "org.junit.jupiter" && it.name == "junit-jupiter-api" }, + ) ?: return null // If found, identify a particular framework version. // For non-numeric versions (i.e. '+'), use the latest version instead - val majorVersion = match.version - ?.substringBefore('.') - ?.toIntOrNull() - val junit = JUnit.entries - .firstOrNull { it.majorVersion == majorVersion } - ?: JUnit.entries.last() + val majorVersion = match.version?.substringBefore('.')?.toIntOrNull() + val junit = + JUnit.entries.firstOrNull { it.majorVersion == majorVersion } ?: JUnit.entries.last() return JUnitUsage(junit) } fun isUsingCompose(configurationName: String): Boolean { - return find( - configurationName, - { it.group?.startsWith("androidx.compose") == true }, - ) != null + return find(configurationName, { it.group?.startsWith("androidx.compose") == true }) != null } - private fun find(configurationName: String, vararg matchers: (Dependency) -> Boolean): Dependency? { + private fun find( + configurationName: String, + vararg matchers: (Dependency) -> Boolean, + ): Dependency? { val configuration = project.configurations.getByName(configurationName) - return configuration.dependencies.firstOrNull { dependency -> matchers.any { it(dependency) } } + return configuration.dependencies.firstOrNull { dependency -> + matchers.any { it(dependency) } + } } } diff --git a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/utils/IncludeExcludeContainer.kt b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/utils/IncludeExcludeContainer.kt index 4b2efc82..e4774a0a 100644 --- a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/utils/IncludeExcludeContainer.kt +++ b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/utils/IncludeExcludeContainer.kt @@ -9,21 +9,22 @@ internal class IncludeExcludeContainer { val include get() = _include.toSet() - fun include(vararg items: String) = this.apply { - this._include.addAll(items) - this._exclude.removeAll(items) - } + fun include(vararg items: String) = + this.apply { + this._include.addAll(items) + this._exclude.removeAll(items) + } val exclude get() = _exclude.toSet() - fun exclude(vararg items: String) = this.apply { - this._exclude.addAll(items) - this._include.removeAll(items) - } + fun exclude(vararg items: String) = + this.apply { + this._exclude.addAll(items) + this._include.removeAll(items) + } - fun isEmpty() = - _include.isEmpty() && _exclude.isEmpty() + fun isEmpty() = _include.isEmpty() && _exclude.isEmpty() operator fun plus(other: IncludeExcludeContainer): IncludeExcludeContainer { // Fast path, where nothing needs to be merged diff --git a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/tasks/AndroidJUnit5JacocoReport.kt b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/tasks/AndroidJUnit5JacocoReport.kt index 9f52ff28..7dfcfaac 100644 --- a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/tasks/AndroidJUnit5JacocoReport.kt +++ b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/tasks/AndroidJUnit5JacocoReport.kt @@ -12,24 +12,22 @@ import de.mannodermaus.gradle.plugins.junit5.internal.extensions.outputLocationF import de.mannodermaus.gradle.plugins.junit5.internal.providers.DirectoryProvider import de.mannodermaus.gradle.plugins.junit5.internal.providers.mainClassDirectories import de.mannodermaus.gradle.plugins.junit5.internal.providers.mainSourceDirectories +import java.io.File import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.file.FileCollection -import org.gradle.api.file.FileSystemLocationProperty -import org.gradle.api.reporting.ConfigurableReport import org.gradle.api.tasks.CacheableTask import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.testing.Test import org.gradle.testing.jacoco.plugins.JacocoTaskExtension import org.gradle.testing.jacoco.tasks.JacocoReport -import java.io.File internal const val JACOCO_TASK_NAME = "jacocoTestReport" 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. + * 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. */ @CacheableTask public abstract class AndroidJUnit5JacocoReport : JacocoReport() { @@ -39,7 +37,7 @@ public abstract class AndroidJUnit5JacocoReport : JacocoReport() { project: Project, variant: Variant, testTask: Test, - directoryProviders: Collection + directoryProviders: Collection, ): TaskProvider? { val configAction = ConfigAction(project, variant, testTask, directoryProviders) project.tasks.namedOrNull(configAction.name)?.let { @@ -47,11 +45,8 @@ public abstract class AndroidJUnit5JacocoReport : JacocoReport() { return null } - val provider = project.tasks.register( - configAction.name, - configAction.type, - configAction::execute - ) + val provider = + project.tasks.register(configAction.name, configAction.type, configAction::execute) // Hook the task into the build chain provider.dependsOn(testTask.name) @@ -62,19 +57,15 @@ public abstract class AndroidJUnit5JacocoReport : JacocoReport() { private fun findOrRegisterDefaultJacocoTask(project: Project): TaskProvider = project.tasks.namedOrNull(JACOCO_TASK_NAME) - ?: project.tasks.register(JACOCO_TASK_NAME) { - it.group = GROUP_REPORTING - } + ?: project.tasks.register(JACOCO_TASK_NAME) { it.group = GROUP_REPORTING } } - /** - * Configuration closure for an Android JUnit5 Jacoco Report task. - */ + /** Configuration closure for an Android JUnit5 Jacoco Report task. */ private class ConfigAction( val project: Project, val variant: Variant, val testTask: Test, - private val directoryProviders: Collection + private val directoryProviders: Collection, ) { val name: String = variant.getTaskName(prefix = JACOCO_TASK_NAME) @@ -85,16 +76,18 @@ public abstract class AndroidJUnit5JacocoReport : JacocoReport() { // Project-level configuration reportTask.dependsOn(testTask) reportTask.group = GROUP_REPORTING - reportTask.description = "Generates Jacoco coverage reports " + + reportTask.description = + "Generates Jacoco coverage reports " + "for the ${variant.name.capitalized()} variant." // Apply JUnit 5 configuration parameters val junit5Jacoco = project.junitPlatform.jacocoOptions - val allReports = listOf( - junit5Jacoco.csv to reportTask.reports.csv, - junit5Jacoco.xml to reportTask.reports.xml, - junit5Jacoco.html to reportTask.reports.html - ) + 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.required.set(from.enabled) @@ -103,18 +96,16 @@ public abstract class AndroidJUnit5JacocoReport : JacocoReport() { // Task-level Configuration val taskJacoco = testTask.extensionByName("jacoco") - taskJacoco.destinationFile?.let { - reportTask.executionData.setFrom(it.path) - } + taskJacoco.destinationFile?.let { reportTask.executionData.setFrom(it.path) } // Apply exclusion rules to both class & source directories for Jacoco, // using the sum of all DirectoryProviders' outputs as a foundation: reportTask.classDirectories.setFrom( - directoryProviders.mainClassDirectories().toFileCollectionExcluding(junit5Jacoco.excludedClasses.get()) - ) - reportTask.sourceDirectories.setFrom( - directoryProviders.mainSourceDirectories() + directoryProviders + .mainClassDirectories() + .toFileCollectionExcluding(junit5Jacoco.excludedClasses.get()) ) + reportTask.sourceDirectories.setFrom(directoryProviders.mainSourceDirectories()) project.logger.junit5Info( "Assembled Jacoco Code Coverage for JUnit 5 Task '${testTask.name}':" @@ -127,13 +118,16 @@ public abstract class AndroidJUnit5JacocoReport : JacocoReport() { /* Extension Functions */ /** - * Joins the given collection of Files together, while - * ignoring the provided patterns in the resulting FileCollection. + * Joins the given collection of Files together, while ignoring the provided patterns in the + * resulting FileCollection. */ - private fun Iterable.toFileCollectionExcluding(patterns: Iterable): FileCollection = this - // Convert each directory to a Gradle FileTree, excluding the specified patterns - .map { project.fileTree(it).exclude(patterns) } - // Convert the resulting list of FileTree objects into a single FileCollection - .run { project.files(this) } + private fun Iterable.toFileCollectionExcluding( + patterns: Iterable + ): FileCollection = + this + // Convert each directory to a Gradle FileTree, excluding the specified patterns + .map { project.fileTree(it).exclude(patterns) } + // Convert the resulting list of FileTree objects into a single FileCollection + .run { project.files(this) } } } diff --git a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/tasks/AndroidJUnit5WriteFilters.kt b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/tasks/AndroidJUnit5WriteFilters.kt index 4c2a90ae..2dae276d 100644 --- a/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/tasks/AndroidJUnit5WriteFilters.kt +++ b/plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/tasks/AndroidJUnit5WriteFilters.kt @@ -6,6 +6,7 @@ import de.mannodermaus.gradle.plugins.junit5.internal.config.INSTRUMENTATION_FIL import de.mannodermaus.gradle.plugins.junit5.internal.config.JUnitPlatformTaskConfig import de.mannodermaus.gradle.plugins.junit5.internal.extensions.getTaskName import de.mannodermaus.gradle.plugins.junit5.internal.extensions.junitPlatform +import java.io.File import org.gradle.api.DefaultTask import org.gradle.api.Project import org.gradle.api.file.DirectoryProperty @@ -14,19 +15,16 @@ import org.gradle.api.tasks.CacheableTask import org.gradle.api.tasks.Input import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction -import java.io.File /** - * Helper task for instrumentation tests. - * It writes out the filters configured through the Gradle plugin's DSL - * into a resource file, used at runtime to set up the execution of the JUnit Platform. + * Helper task for instrumentation tests. It writes out the filters configured through the Gradle + * plugin's DSL into a resource file, used at runtime to set up the execution of the JUnit Platform. * - * Note: - * This only allows tests to be filtered with @Tag annotations even in the instrumentation test realm. - * Other plugin DSL settings, like includeEngines/excludeEngines or includePattern/excludePattern - * are not copied out to file. This has to do with limitations of the backport implementation - * of the JUnit Platform Runner, as well as some incompatibilities between Gradle and Java - * regarding how class name patterns are formatted. + * Note: This only allows tests to be filtered with @Tag annotations even in the instrumentation + * test realm. Other plugin DSL settings, like includeEngines/excludeEngines or + * includePattern/excludePattern are not copied out to file. This has to do with limitations of the + * backport implementation of the JUnit Platform Runner, as well as some incompatibilities between + * Gradle and Java regarding how class name patterns are formatted. */ @CacheableTask public abstract class AndroidJUnit5WriteFilters : DefaultTask() { @@ -39,11 +37,8 @@ public abstract class AndroidJUnit5WriteFilters : DefaultTask() { ): Boolean { val configAction = ConfigAction(project, variant) - val provider = project.tasks.register( - configAction.name, - configAction.type, - configAction::execute - ) + val provider = + project.tasks.register(configAction.name, configAction.type, configAction::execute) // Connect the output folder of the task to the instrumentation tests // so that they are bundled into the built test application @@ -56,14 +51,11 @@ public abstract class AndroidJUnit5WriteFilters : DefaultTask() { } } - @get:Input - public abstract val includeTags: ListProperty + @get:Input public abstract val includeTags: ListProperty - @get:Input - public abstract val excludeTags: ListProperty + @get:Input public abstract val excludeTags: ListProperty - @get:OutputDirectory - public abstract val outputFolder: DirectoryProperty + @get:OutputDirectory public abstract val outputFolder: DirectoryProperty @TaskAction public fun execute() { @@ -81,20 +73,15 @@ public abstract class AndroidJUnit5WriteFilters : DefaultTask() { // the generated file will have a fixed name & is located // as a "raw" resource inside the output folder val rawFolder = File(folder, "raw").apply { mkdirs() } - File(rawFolder, INSTRUMENTATION_FILTER_RES_FILE_NAME) - .bufferedWriter() - .use { writer -> - // This format is a nod towards the real JUnit 5 ConsoleLauncher's arguments - includeTags.forEach { tag -> writer.appendLine("-t $tag") } - excludeTags.forEach { tag -> writer.appendLine("-T $tag") } - } + File(rawFolder, INSTRUMENTATION_FILTER_RES_FILE_NAME).bufferedWriter().use { writer -> + // This format is a nod towards the real JUnit 5 ConsoleLauncher's arguments + includeTags.forEach { tag -> writer.appendLine("-t $tag") } + excludeTags.forEach { tag -> writer.appendLine("-T $tag") } + } } } - private class ConfigAction( - private val project: Project, - private val variant: Variant, - ) { + private class ConfigAction(private val project: Project, private val variant: Variant) { val name: String = variant.getTaskName(prefix = "writeFilters", suffix = "androidTest") @@ -106,7 +93,8 @@ public abstract class AndroidJUnit5WriteFilters : DefaultTask() { task.includeTags.set(configuration.combinedIncludeTags.toList()) task.excludeTags.set(configuration.combinedExcludeTags.toList()) - // Output folder is applied by Android Gradle Plugin, so there is no reason to provide a value ourselves + // Output folder is applied by Android Gradle Plugin, so there is no reason to provide a + // value ourselves } } } diff --git a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/ConfigurationCacheTests.kt b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/ConfigurationCacheTests.kt index 7a5ba3f5..055a2919 100644 --- a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/ConfigurationCacheTests.kt +++ b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/ConfigurationCacheTests.kt @@ -7,6 +7,7 @@ import de.mannodermaus.gradle.plugins.junit5.util.prettyPrint import de.mannodermaus.gradle.plugins.junit5.util.projects.FunctionalTestProjectCreator import de.mannodermaus.gradle.plugins.junit5.util.splitToArray import de.mannodermaus.gradle.plugins.junit5.util.withPrunedPluginClasspath +import java.io.File import org.gradle.testkit.runner.BuildResult import org.gradle.testkit.runner.GradleRunner import org.gradle.testkit.runner.TaskOutcome.FAILED @@ -17,7 +18,6 @@ import org.junit.jupiter.api.TestFactory import org.junit.jupiter.api.TestInstance import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS import org.junit.jupiter.api.io.TempDir -import java.io.File @TestInstance(PER_CLASS) @DisabledOnCI @@ -38,39 +38,41 @@ class ConfigurationCacheTests { } @TestFactory - fun `test instrumentation tasks`() = environment.supportedJUnitVersions.map { junit -> - dynamicTest("JUnit ${junit.majorVersion}") { - // Test configuration cache with one specific project and AGP version - val spec = projectCreator.specNamed("instrumentation-tests") - val project = projectCreator.createProject(spec, agp, junit) + fun `test instrumentation tasks`() = + environment.supportedJUnitVersions.map { junit -> + dynamicTest("JUnit ${junit.majorVersion}") { + // Test configuration cache with one specific project and AGP version + val spec = projectCreator.specNamed("instrumentation-tests") + val project = projectCreator.createProject(spec, agp, junit) - // Run it once; this is supposed to fail, but JUST because of 'no connected device', - // not because of other errors including the configuration cache. - runGradle(project, "connectedCheck", expectSuccess = false).assertWithLogging { - assertThat(it).task(":connectedDebugAndroidTest").hasOutcome(FAILED) - assertThat(it).output().contains("DeviceException: No connected devices!") - } + // Run it once; this is supposed to fail, but JUST because of 'no connected device', + // not because of other errors including the configuration cache. + runGradle(project, "connectedCheck", expectSuccess = false).assertWithLogging { + assertThat(it).task(":connectedDebugAndroidTest").hasOutcome(FAILED) + assertThat(it).output().contains("DeviceException: No connected devices!") + } - // Run it again, expecting to see a successful reuse of the configuration cache - runGradle(project, "connectedCheck", expectSuccess = false).assertWithLogging { - assertThat(it).output().contains("Reusing configuration cache.") + // Run it again, expecting to see a successful reuse of the configuration cache + runGradle(project, "connectedCheck", expectSuccess = false).assertWithLogging { + assertThat(it).output().contains("Reusing configuration cache.") + } } } - } @TestFactory - fun `test unit tasks`() = environment.supportedJUnitVersions.map { junit -> - val spec = projectCreator.specNamed("product-flavors") - val project = projectCreator.createProject(spec, agp, junit) + fun `test unit tasks`() = + environment.supportedJUnitVersions.map { junit -> + val spec = projectCreator.specNamed("product-flavors") + val project = projectCreator.createProject(spec, agp, junit) - runGradle(project, "help", expectSuccess = true).assertWithLogging { - assertThat(it).task(":help").hasOutcome(SUCCESS) - } + runGradle(project, "help", expectSuccess = true).assertWithLogging { + assertThat(it).task(":help").hasOutcome(SUCCESS) + } - runGradle(project, "help", expectSuccess = true).assertWithLogging { - assertThat(it).output().contains("Reusing configuration cache.") + runGradle(project, "help", expectSuccess = true).assertWithLogging { + assertThat(it).output().contains("Reusing configuration cache.") + } } - } /* Private */ @@ -80,10 +82,7 @@ class ConfigurationCacheTests { .withGradleVersion(agp.requiresGradle) .withArguments("--configuration-cache", "--stacktrace", task) .withPrunedPluginClasspath(agp) - .run { - if (expectSuccess) build() - else buildAndFail() - } + .run { if (expectSuccess) build() else buildAndFail() } private fun BuildResult.assertWithLogging(block: (BuildResult) -> Unit) { try { diff --git a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/FunctionalTests.kt b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/FunctionalTests.kt index 1c8dbb07..6b6ef12b 100644 --- a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/FunctionalTests.kt +++ b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/FunctionalTests.kt @@ -10,6 +10,7 @@ import de.mannodermaus.gradle.plugins.junit5.util.TestedJUnit import de.mannodermaus.gradle.plugins.junit5.util.prettyPrint import de.mannodermaus.gradle.plugins.junit5.util.projects.FunctionalTestProjectCreator import de.mannodermaus.gradle.plugins.junit5.util.withPrunedPluginClasspath +import java.io.File import org.gradle.testkit.runner.BuildResult import org.gradle.testkit.runner.GradleRunner import org.gradle.testkit.runner.TaskOutcome @@ -22,7 +23,6 @@ import org.junit.jupiter.api.TestFactory import org.junit.jupiter.api.TestInstance import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS import org.junit.jupiter.api.fail -import java.io.File @TestInstance(PER_CLASS) @DisabledOnCI @@ -31,16 +31,13 @@ class FunctionalTests { private lateinit var folder: File // Test permutations for AGP (default: empty set, which will exercise all) - private val testedAgpVersions: Set = setOf( - ) + private val testedAgpVersions: Set = setOf() // Test permutations for JUnit (default: empty set, which will exercise all) - private val testedJUnitVersions: Set = setOf( - ) + private val testedJUnitVersions: Set = setOf() // Test permutations for projects (default: empty set, which will exercise all) - private val testedProjects: Set = setOf( - ) + private val testedProjects: Set = setOf() // Whether to pass "-i" to the Gradle runners, increasing insight into their output private val verboseOutput = false @@ -84,24 +81,27 @@ class FunctionalTests { dynamicTest("${spec.name} ($junit)") { // Required for visibility inside the IntelliJ logging console // (display names are still bugged in the IDE) - println(buildList { - add("JUnit ${junit.majorVersion}") - add("AGP: ${agp.version}") - add("Project: ${spec.name}") - add("Gradle: ${agp.requiresGradle}") - agp.requiresCompileSdk?.let { add("SDK: $it") } - }.joinToString()) + println( + buildList { + add("JUnit ${junit.majorVersion}") + add("AGP: ${agp.version}") + add("Project: ${spec.name}") + add("Gradle: ${agp.requiresGradle}") + agp.requiresCompileSdk?.let { add("SDK: $it") } + } + .joinToString() + ) // Create a virtual project with the given settings & AGP version. - // This call will throw a TestAbortedException if the spec is not eligible for this version, + // This call will throw a TestAbortedException if the spec is not + // eligible for this version, // marking the test as ignored in the process val project = projectCreator.createProject(spec, agp, junit) // Execute the tests of the virtual project with Gradle val taskName = spec.task ?: "test" - val result = runGradle(agp, taskName) - .withProjectDir(project) - .build() + val result = + runGradle(agp, taskName).withProjectDir(project).build() // Print Gradle logs from the embedded invocation result.prettyPrint() @@ -116,30 +116,34 @@ class FunctionalTests { outcome == TaskOutcome.SUCCESS -> { // Based on the spec's configuration in the test project, - // assert that all test classes have been executed as expected + // assert that all test classes have been executed as + // expected for (expectation in spec.expectedTests) { result.assertAgpTests( buildType = expectation.buildType, productFlavor = expectation.productFlavor, - tests = expectation.testsList + tests = expectation.testsList, ) } } outcome == TaskOutcome.SKIPPED && spec.allowSkipped -> { - // It might be acceptable to allow "skipped" as the result depending on the test spec + // It might be acceptable to allow "skipped" as the result + // depending on the test spec println("Task '$taskName' was skipped.") } else -> { // Unexpected result; fail - fail { "Unexpected task outcome: $outcome\n\nRaw output:\n\n${result.output}" } + fail { + "Unexpected task outcome: $outcome\n\nRaw output:\n\n${result.output}" + } } } } - } + }, ) - } + }, ) } } @@ -152,9 +156,7 @@ class FunctionalTests { // (but in reverse order, so that the newest AGP is tested first) reversed() } else { - filter { agp -> - testedAgpVersions.any { it == agp.shortVersion } - } + filter { agp -> testedAgpVersions.any { it == agp.shortVersion } } } private fun List.filterJUnitVersions(): List = @@ -163,19 +165,16 @@ class FunctionalTests { // (but in reverse order, so that the newest JUnit is tested first) reversed() } else { - filter { junit -> - testedJUnitVersions.any { it == junit.majorVersion } - } + filter { junit -> testedJUnitVersions.any { it == junit.majorVersion } } } - private fun List.filterSpecs(): List = + private fun List.filterSpecs(): + List = if (testedProjects.isEmpty()) { // Nothing to do, exercise all different projects this } else { - filter { spec -> - testedProjects.any { it == spec.name } - } + filter { spec -> testedProjects.any { it == spec.name } } } private fun runGradle(agpVersion: TestedAgp, task: String): GradleRunner { @@ -196,13 +195,14 @@ class FunctionalTests { private fun BuildResult.assertAgpTests( buildType: String, productFlavor: String? = null, - tests: List + tests: List, ) { // Construct task name from given build type and/or product flavor // Examples: // - buildType="debug", productFlavor=null --> ":testDebugUnitTest" // - buildType="debug", productFlavor="free" --> ":testFreeDebugUnitTest" - val taskName = ":test${productFlavor?.capitalized() ?: ""}${buildType.capitalized()}UnitTest" + val taskName = + ":test${productFlavor?.capitalized() ?: ""}${buildType.capitalized()}UnitTest" // Perform assertions assertWithMessage("AGP Tests for '$taskName' did not match expectations") diff --git a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/IncludeExcludeContainerTests.kt b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/IncludeExcludeContainerTests.kt index 14a47626..9b79906c 100644 --- a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/IncludeExcludeContainerTests.kt +++ b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/IncludeExcludeContainerTests.kt @@ -4,17 +4,16 @@ import com.google.common.truth.Truth.assertThat import de.mannodermaus.gradle.plugins.junit5.internal.utils.IncludeExcludeContainer import org.junit.jupiter.api.Test -/** - * Created by Marcel Schnelle on 2018/06/28. - */ +/** Created by Marcel Schnelle on 2018/06/28. */ class IncludeExcludeContainerTests { @Test fun `adding an include rule will remove an existing exclude rule`() { - val container = IncludeExcludeContainer().apply { - exclude("slow") - include("slow") - } + val container = + IncludeExcludeContainer().apply { + exclude("slow") + include("slow") + } assertThat(container.include).containsExactly("slow") assertThat(container.exclude).isEmpty() @@ -22,10 +21,11 @@ class IncludeExcludeContainerTests { @Test fun `adding an exclude rule will remove an existing include rule`() { - val container = IncludeExcludeContainer().apply { - include("slow") - exclude("slow") - } + val container = + IncludeExcludeContainer().apply { + include("slow") + exclude("slow") + } assertThat(container.include).isEmpty() assertThat(container.exclude).containsExactly("slow") @@ -33,20 +33,22 @@ class IncludeExcludeContainerTests { @Test fun `adding the same include rule twice will only add a single entry`() { - val container = IncludeExcludeContainer().apply { - include("slow") - include("slow") - } + val container = + IncludeExcludeContainer().apply { + include("slow") + include("slow") + } assertThat(container.include).containsExactly("slow") } @Test fun `adding the same exclude rule twice will only add a single entry`() { - val container = IncludeExcludeContainer().apply { - exclude("slow") - exclude("slow") - } + val container = + IncludeExcludeContainer().apply { + exclude("slow") + exclude("slow") + } assertThat(container.exclude).containsExactly("slow") } @@ -71,9 +73,7 @@ class IncludeExcludeContainerTests { @Test fun `adding an empty container returns the original one`() { - val container1 = IncludeExcludeContainer().apply { - include("slow") - } + val container1 = IncludeExcludeContainer().apply { include("slow") } val container2 = IncludeExcludeContainer() val merged = container1 + container2 assertThat(merged).isEqualTo(container1) @@ -82,21 +82,15 @@ class IncludeExcludeContainerTests { @Test fun `adding something to an empty container returns the new one`() { val container1 = IncludeExcludeContainer() - val container2 = IncludeExcludeContainer().apply { - include("slow") - } + val container2 = IncludeExcludeContainer().apply { include("slow") } val merged = container1 + container2 assertThat(merged).isEqualTo(container2) } @Test fun `adding two conainers will merge the include rules together`() { - val container1 = IncludeExcludeContainer().apply { - include("slow") - } - val container2 = IncludeExcludeContainer().apply { - include("fast") - } + val container1 = IncludeExcludeContainer().apply { include("slow") } + val container2 = IncludeExcludeContainer().apply { include("fast") } val merged = container1 + container2 assertThat(merged.include).containsExactly("slow", "fast") @@ -105,12 +99,8 @@ class IncludeExcludeContainerTests { @Test fun `adding two containers will merge the exclude rules together`() { - val container1 = IncludeExcludeContainer().apply { - exclude("slow") - } - val container2 = IncludeExcludeContainer().apply { - exclude("fast") - } + val container1 = IncludeExcludeContainer().apply { exclude("slow") } + val container2 = IncludeExcludeContainer().apply { exclude("fast") } val merged = container1 + container2 assertThat(merged.include).isEmpty() @@ -119,12 +109,8 @@ class IncludeExcludeContainerTests { @Test fun `adding two containers will remove an existing include rule with a second object's exclude rule`() { - val container1 = IncludeExcludeContainer().apply { - include("slow") - } - val container2 = IncludeExcludeContainer().apply { - exclude("slow") - } + val container1 = IncludeExcludeContainer().apply { include("slow") } + val container2 = IncludeExcludeContainer().apply { exclude("slow") } val merged = container1 + container2 assertThat(merged.include).isEmpty() @@ -133,12 +119,8 @@ class IncludeExcludeContainerTests { @Test fun `adding two containers will remove an existing exclude rule with a second object's include rule`() { - val container1 = IncludeExcludeContainer().apply { - exclude("slow") - } - val container2 = IncludeExcludeContainer().apply { - include("slow") - } + val container1 = IncludeExcludeContainer().apply { exclude("slow") } + val container2 = IncludeExcludeContainer().apply { include("slow") } val merged = container1 + container2 assertThat(merged.include).containsExactly("slow") @@ -147,14 +129,16 @@ class IncludeExcludeContainerTests { @Test fun `adding two containers won't touch unrelated rules`() { - val container1 = IncludeExcludeContainer().apply { - include("fast") - include("slow") - } - val container2 = IncludeExcludeContainer().apply { - exclude("another") - exclude("slow") - } + val container1 = + IncludeExcludeContainer().apply { + include("fast") + include("slow") + } + val container2 = + IncludeExcludeContainer().apply { + exclude("another") + exclude("slow") + } val merged = container1 + container2 assertThat(merged.include).containsExactly("fast") diff --git a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/annotations/DisabledOnCI.kt b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/annotations/DisabledOnCI.kt index e1a35f70..7aad6d11 100644 --- a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/annotations/DisabledOnCI.kt +++ b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/annotations/DisabledOnCI.kt @@ -2,5 +2,4 @@ package de.mannodermaus.gradle.plugins.junit5.annotations import org.junit.jupiter.api.condition.DisabledIfEnvironmentVariable -@DisabledIfEnvironmentVariable(named = "CI", matches = "true") -annotation class DisabledOnCI +@DisabledIfEnvironmentVariable(named = "CI", matches = "true") annotation class DisabledOnCI diff --git a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AbstractProjectTests.kt b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AbstractProjectTests.kt index 1eb2bffe..18b08698 100644 --- a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AbstractProjectTests.kt +++ b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AbstractProjectTests.kt @@ -7,37 +7,34 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.RegisterExtension /** - * Common baseline for AGP-based testing. Different subclasses extend this - * for every type of Android Gradle Plugin supported by JUnit 5. + * Common baseline for AGP-based testing. Different subclasses extend this for every type of Android + * Gradle Plugin supported by JUnit 5. */ abstract class AbstractProjectTests( - private val pluginApplier: ((PluginSpecProjectCreator.Builder) -> PluginSpecProjectCreator.Builder) + private val pluginApplier: + ((PluginSpecProjectCreator.Builder) -> PluginSpecProjectCreator.Builder) ) : - AgpConfigurationParameterTests, - AgpFilterTests, - AgpInstrumentationSupportTests, - AgpJacocoBaseTests, - AgpJacocoExclusionRuleTests, - AgpJacocoVariantTests, - AgpVariantTests { - - @RegisterExtension - @JvmField - val projectExtension = TestProjectProviderExtension() + AgpConfigurationParameterTests, + AgpFilterTests, + AgpInstrumentationSupportTests, + AgpJacocoBaseTests, + AgpJacocoExclusionRuleTests, + AgpJacocoVariantTests, + AgpVariantTests { + + @RegisterExtension @JvmField val projectExtension = TestProjectProviderExtension() override fun createProject(): PluginSpecProjectCreator.Builder { - return projectExtension.newProject() - .also { pluginApplier(it) } + return projectExtension.newProject().also { pluginApplier(it) } } - override fun defaultBuildTypes() = listOf( - "debug", "release" - ) + override fun defaultBuildTypes() = listOf("debug", "release") - override fun defaultProductFlavors() = listOf( + override fun defaultProductFlavors() = + listOf( FlavorSpec(name = "free", dimension = "tier"), - FlavorSpec(name = "paid", dimension = "tier") - ) + FlavorSpec(name = "paid", dimension = "tier"), + ) @Test fun `add an extension to testOptions`() { diff --git a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AgpConfigurationParameterTests.kt b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AgpConfigurationParameterTests.kt index 554987d5..a95461c3 100644 --- a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AgpConfigurationParameterTests.kt +++ b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AgpConfigurationParameterTests.kt @@ -4,16 +4,16 @@ import com.google.common.truth.Truth.assertThat import de.mannodermaus.gradle.plugins.junit5.internal.extensions.junitPlatform import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows -import org.junit.platform.commons.PreconditionViolationException interface AgpConfigurationParameterTests : AgpTests { @Test fun `throw exception if configuration parameter key is empty`() { val project = createProject().build() - val exception = assertThrows { - project.junitPlatform.configurationParameter("", "some-value") - } + val exception = + assertThrows { + project.junitPlatform.configurationParameter("", "some-value") + } assertThat(exception.message).contains("key must not be blank") } @@ -21,9 +21,10 @@ interface AgpConfigurationParameterTests : AgpTests { fun `throw exception if configuration parameter key contains illegal characters`() { val project = createProject().build() - val exception = assertThrows { - project.junitPlatform.configurationParameter("illegal=key", "some-value") - } + val exception = + assertThrows { + project.junitPlatform.configurationParameter("illegal=key", "some-value") + } assertThat(exception.message).contains("key must not contain '='") } } diff --git a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AgpFilterTests.kt b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AgpFilterTests.kt index 78e5090a..64b2938d 100644 --- a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AgpFilterTests.kt +++ b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AgpFilterTests.kt @@ -24,34 +24,33 @@ interface AgpFilterTests : AgpVariantAwareTests { } @TestFactory - fun `apply global filter configuration correctly`() = forEachBuildType( - beforeEvaluate = { project -> - project.junitPlatform.filters { - it.includeTags("global-include-tag") - it.excludeTags("global-exclude-tag") - it.includeEngines("global-include-engine") - it.excludeEngines("global-exclude-engine") - it.includePattern("com.example.package1") - it.excludePattern("com.example.package2") + fun `apply global filter configuration correctly`() = + forEachBuildType( + beforeEvaluate = { project -> + project.junitPlatform.filters { + it.includeTags("global-include-tag") + it.excludeTags("global-exclude-tag") + it.includeEngines("global-include-engine") + it.excludeEngines("global-exclude-engine") + it.includePattern("com.example.package1") + it.excludePattern("com.example.package2") + } } + ) { project, buildType -> + val task = project.tasks.get("test${buildType.capitalized()}UnitTest") + assertThat(task.junitPlatformOptions.includeTags).contains("global-include-tag") + assertThat(task.junitPlatformOptions.excludeTags).contains("global-exclude-tag") + assertThat(task.junitPlatformOptions.includeEngines).contains("global-include-engine") + assertThat(task.junitPlatformOptions.excludeEngines).contains("global-exclude-engine") + assertThat(task.includes).contains("com.example.package1") + assertThat(task.excludes).contains("com.example.package2") } - ) { project, buildType -> - val task = project.tasks.get("test${buildType.capitalized()}UnitTest") - assertThat(task.junitPlatformOptions.includeTags).contains("global-include-tag") - assertThat(task.junitPlatformOptions.excludeTags).contains("global-exclude-tag") - assertThat(task.junitPlatformOptions.includeEngines).contains("global-include-engine") - assertThat(task.junitPlatformOptions.excludeEngines).contains("global-exclude-engine") - assertThat(task.includes).contains("com.example.package1") - assertThat(task.excludes).contains("com.example.package2") - } @TestFactory fun `using custom build types & multiple flavor dimensions`(): List { val project = createProject().build() project.registerProductFlavors(advancedFlavorList) - with(project.android.buildTypes) { - create("ci").initWith(getByName("debug")) - } + with(project.android.buildTypes) { create("ci").initWith(getByName("debug")) } project.evaluate() return advancedFilterDslNames.map { filterName -> @@ -76,9 +75,7 @@ interface AgpFilterTests : AgpVariantAwareTests { it.includePattern("com.example.paid") it.excludePattern("com.example.package1") } - filters("freeDebug") { - it.includeTags("freeDebug-include-tag") - } + filters("freeDebug") { it.includeTags("freeDebug-include-tag") } filters("paidRelease") { it.includeTags("paidRelease-include-tag") it.includeTags("global-exclude-tag") @@ -94,8 +91,7 @@ interface AgpFilterTests : AgpVariantAwareTests { .containsAtLeast("global-include-tag", "freeDebug-include-tag") assertThat(task.junitPlatformOptions.includeTags) .doesNotContain("paidRelease-include-tag") - assertThat(task.junitPlatformOptions.excludeTags) - .contains("global-exclude-tag") + assertThat(task.junitPlatformOptions.excludeTags).contains("global-exclude-tag") assertThat(task.junitPlatformOptions.includeEngines) .doesNotContain("paid-include-engine") @@ -104,17 +100,14 @@ interface AgpFilterTests : AgpVariantAwareTests { assertThat(task.includes).doesNotContain("com.example.paid") assertThat(task.includes).doesNotContain("com.example.paid.release") }, - dynamicTest("apply freeRelease filters correctly") { val task = project.tasks.get("testFreeReleaseUnitTest") - assertThat(task.junitPlatformOptions.includeTags) - .contains("global-include-tag") + assertThat(task.junitPlatformOptions.includeTags).contains("global-include-tag") assertThat(task.junitPlatformOptions.includeTags) .doesNotContain("freeDebug-include-tag") assertThat(task.junitPlatformOptions.includeTags) .doesNotContain("paidRelease-include-tag") - assertThat(task.junitPlatformOptions.excludeTags) - .contains("global-exclude-tag") + assertThat(task.junitPlatformOptions.excludeTags).contains("global-exclude-tag") assertThat(task.junitPlatformOptions.includeEngines) .doesNotContain("paid-include-engine") @@ -123,52 +116,42 @@ interface AgpFilterTests : AgpVariantAwareTests { assertThat(task.includes).doesNotContain("com.example.paid") assertThat(task.includes).doesNotContain("com.example.paid.release") }, - dynamicTest("apply paidDebug filters correctly") { val task = project.tasks.get("testPaidDebugUnitTest") + assertThat(task.junitPlatformOptions.includeTags).contains("global-include-tag") assertThat(task.junitPlatformOptions.includeTags) - .contains("global-include-tag") - assertThat(task.junitPlatformOptions.includeTags) - .doesNotContain( - "freeDebug-include-tag" - ) + .doesNotContain("freeDebug-include-tag") assertThat(task.junitPlatformOptions.includeTags) - .doesNotContain( - "paidRelease-include-tag" - ) - assertThat(task.junitPlatformOptions.excludeTags) - .contains("global-exclude-tag") + .doesNotContain("paidRelease-include-tag") + assertThat(task.junitPlatformOptions.excludeTags).contains("global-exclude-tag") - assertThat(task.junitPlatformOptions.includeEngines) - .contains("paid-include-engine") + assertThat(task.junitPlatformOptions.includeEngines).contains("paid-include-engine") assertThat(task.includes).contains("com.example.paid") assertThat(task.excludes).contains("com.example.package1") assertThat(task.includes).doesNotContain("com.example.package1") assertThat(task.includes).doesNotContain("com.example.paid.release") }, - dynamicTest("apply paidRelease filters correctly") { val task = project.tasks.get("testPaidReleaseUnitTest") assertThat(task.junitPlatformOptions.includeTags) .containsAtLeast( "global-include-tag", "global-exclude-tag", - "paidRelease-include-tag" + "paidRelease-include-tag", ) assertThat(task.junitPlatformOptions.includeTags) .doesNotContain("freeDebug-include-tag") assertThat(task.junitPlatformOptions.excludeTags) .doesNotContain("global-exclude-tag") - assertThat(task.junitPlatformOptions.includeEngines) - .contains("paid-include-engine") + assertThat(task.junitPlatformOptions.includeEngines).contains("paid-include-engine") assertThat(task.includes) .containsAtLeast("com.example.paid", "com.example.paid.release") assertThat(task.includes).doesNotContain("com.example.package1") assertThat(task.excludes).contains("com.example.package1") - } + }, ) } @@ -203,34 +186,34 @@ interface AgpFilterTests : AgpVariantAwareTests { assertThat(task.junitPlatformOptions.includeTags).doesNotContain("rel-include-tag") assertThat(task.junitPlatformOptions.excludeTags).contains("debug-exclude-tag") - assertThat(task.junitPlatformOptions.includeEngines).contains("global-include-engine") - assertThat(task.junitPlatformOptions.includeEngines).doesNotContain( - "rel-include-engine" - ) - assertThat(task.junitPlatformOptions.excludeEngines).contains("debug-exclude-engine") + assertThat(task.junitPlatformOptions.includeEngines) + .contains("global-include-engine") + assertThat(task.junitPlatformOptions.includeEngines) + .doesNotContain("rel-include-engine") + assertThat(task.junitPlatformOptions.excludeEngines) + .contains("debug-exclude-engine") assertThat(task.includes).doesNotContain("pattern123") assertThat(task.excludes).containsAtLeast("pattern123", "debug-pattern") }, - dynamicTest("apply release filters correctly") { val task = project.tasks.get("testReleaseUnitTest") assertThat(task.junitPlatformOptions.includeTags) .containsAtLeast("global-include-tag", "rel-include-tag") - assertThat(task.junitPlatformOptions.excludeTags).doesNotContain("debug-exclude-tag") + assertThat(task.junitPlatformOptions.excludeTags) + .doesNotContain("debug-exclude-tag") assertThat(task.junitPlatformOptions.includeEngines).contains("rel-include-engine") - assertThat(task.junitPlatformOptions.includeEngines).doesNotContain( - "global-include-engine" - ) - assertThat(task.junitPlatformOptions.excludeEngines).contains("global-include-engine") - assertThat(task.junitPlatformOptions.excludeEngines).doesNotContain( - "debug-exclude-engine" - ) + assertThat(task.junitPlatformOptions.includeEngines) + .doesNotContain("global-include-engine") + assertThat(task.junitPlatformOptions.excludeEngines) + .contains("global-include-engine") + assertThat(task.junitPlatformOptions.excludeEngines) + .doesNotContain("debug-exclude-engine") assertThat(task.includes).containsAtLeast("pattern123", "release-pattern") assertThat(task.excludes).doesNotContain("pattern123") - } + }, ) } } @@ -242,26 +225,21 @@ private val advancedFlavorList = FlavorSpec(name = "development", dimension = "environment"), FlavorSpec(name = "production", dimension = "environment"), FlavorSpec(name = "free", dimension = "payment"), - FlavorSpec(name = "paid", dimension = "payment") + FlavorSpec(name = "paid", dimension = "payment"), ) private val advancedFilterDslNames = listOf( "filters", - "debugFilters", "releaseFilters", "ciFilters", - "brandAFilters", "brandBFilters", - "developmentFilters", "productionFilters", - "freeFilters", "paidFilters", - "brandADevelopmentPaidDebugFilters", "brandADevelopmentPaidReleaseFilters", "brandADevelopmentPaidCiFilters", @@ -274,7 +252,6 @@ private val advancedFilterDslNames = "brandAProductionFreeDebugFilters", "brandAProductionFreeReleaseFilters", "brandAProductionFreeCiFilters", - "brandBDevelopmentPaidDebugFilters", "brandBDevelopmentPaidReleaseFilters", "brandBDevelopmentPaidCiFilters", @@ -286,5 +263,5 @@ private val advancedFilterDslNames = "brandBProductionPaidCiFilters", "brandBProductionFreeDebugFilters", "brandBProductionFreeReleaseFilters", - "brandBProductionFreeCiFilters" + "brandBProductionFreeCiFilters", ) diff --git a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AgpInstrumentationSupportTests.kt b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AgpInstrumentationSupportTests.kt index 7f411c08..083182fe 100644 --- a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AgpInstrumentationSupportTests.kt +++ b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AgpInstrumentationSupportTests.kt @@ -42,18 +42,18 @@ interface AgpInstrumentationSupportTests : AgpVariantAwareTests { return listOf( dynamicTest("has a task for writing the debug filters DSL to a resource file") { - val task = project.tasks.get("writeFiltersDebugAndroidTest") + val task = + project.tasks.get("writeFiltersDebugAndroidTest") assertAll( { assertThat(task).isNotNull() }, { assertThat(task.includeTags.get()).containsExactly("global-include-tag") }, - { assertThat(task.excludeTags.get()).containsExactly("debug-exclude-tag") } + { assertThat(task.excludeTags.get()).containsExactly("debug-exclude-tag") }, ) }, - dynamicTest("has no task for writing the release DSL to a resource file") { val task = project.tasks.findByName("writeFiltersReleaseAndroidTest") assertThat(task).isNull() - } + }, ) } @@ -84,9 +84,7 @@ interface AgpInstrumentationSupportTests : AgpVariantAwareTests { it.includePattern("com.example.paid") it.excludePattern("com.example.package1") } - filters("freeDebug") { - it.includeTags("freeDebug-include-tag") - } + filters("freeDebug") { it.includeTags("freeDebug-include-tag") } filters("paidRelease") { it.includeTags("paidRelease-include-tag") it.includeTags("global-exclude-tag") @@ -97,30 +95,34 @@ interface AgpInstrumentationSupportTests : AgpVariantAwareTests { return listOf( dynamicTest("has a task for writing the freeDebug filters DSL to a resource file") { - val task = project.tasks.get("writeFiltersFreeDebugAndroidTest") + val task = + project.tasks.get("writeFiltersFreeDebugAndroidTest") assertThat(task).isNotNull() - assertThat(task.includeTags.get()).containsExactly("global-include-tag", "freeDebug-include-tag") + assertThat(task.includeTags.get()) + .containsExactly("global-include-tag", "freeDebug-include-tag") assertThat(task.excludeTags.get()).containsExactly("global-exclude-tag") }, - dynamicTest("has a task for writing the paidDebug filters DSL to a resource file") { - val task = project.tasks.get("writeFiltersPaidDebugAndroidTest") + val task = + project.tasks.get("writeFiltersPaidDebugAndroidTest") assertThat(task).isNotNull() assertThat(task.includeTags.get()).containsExactly("global-include-tag") assertThat(task.excludeTags.get()).containsExactly("global-exclude-tag") }, - - dynamicTest("doesn't have tasks for writing the release filters DSL to a resource file") { + dynamicTest( + "doesn't have tasks for writing the release filters DSL to a resource file" + ) { assertThat(project.tasks.findByName("writeFiltersFreeReleaseAndroidTest")).isNull() assertThat(project.tasks.findByName("writeFiltersPaidReleaseAndroidTest")).isNull() - } + }, ) } } private fun Project.setupInstrumentationTests() { android.defaultConfig { - testInstrumentationRunnerArguments["runnerBuilder"] = "de.mannodermaus.junit5.AndroidJUnit5Builder" + testInstrumentationRunnerArguments["runnerBuilder"] = + "de.mannodermaus.junit5.AndroidJUnit5Builder" } dependencies.add("androidTestRuntimeOnly", "de.mannodermaus.junit5:android-test-runner:+") } diff --git a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AgpJacocoBaseTests.kt b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AgpJacocoBaseTests.kt index 56e86b5b..07f1cccb 100644 --- a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AgpJacocoBaseTests.kt +++ b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AgpJacocoBaseTests.kt @@ -39,24 +39,24 @@ interface AgpJacocoBaseTests : AgpVariantAwareTests { project.evaluate() assertThat(project.tasks.findByName(JACOCO_TASK_NAME)).isNotNull() - assertThat(project.tasks.findByName("${JACOCO_TASK_NAME}Debug")).isInstanceOf(JacocoReport::class.java) - assertThat(project.tasks.findByName("${JACOCO_TASK_NAME}Release")).isInstanceOf(AndroidJUnit5JacocoReport::class.java) + assertThat(project.tasks.findByName("${JACOCO_TASK_NAME}Debug")) + .isInstanceOf(JacocoReport::class.java) + assertThat(project.tasks.findByName("${JACOCO_TASK_NAME}Release")) + .isInstanceOf(AndroidJUnit5JacocoReport::class.java) } @TestFactory fun `acknowledge disabling of jacoco task generation`(): List { val project = createProject().applyJacocoPlugin().build() - project.junitPlatform.jacocoOptions { - it.taskGenerationEnabled.set(false) - } + project.junitPlatform.jacocoOptions { it.taskGenerationEnabled.set(false) } project.evaluate() return listOf(JACOCO_TASK_NAME, "${JACOCO_TASK_NAME}Debug", "${JACOCO_TASK_NAME}Release") - .map { task -> - dynamicTest("does not generate $task task if generation is disabled") { - assertThat(project.tasks.findByName(task)).isNull() - } + .map { task -> + dynamicTest("does not generate $task task if generation is disabled") { + assertThat(project.tasks.findByName(task)).isNull() } + } } // Reporting @@ -71,15 +71,22 @@ interface AgpJacocoBaseTests : AgpVariantAwareTests { } project.evaluate() - project.tasks.withType(AndroidJUnit5JacocoReport::class.java) - .map { it.reports } - .forEach { report -> - assertAll( - { assertThat(report.xml.outputLocationFilePath).endsWith("build/other-jacoco-folder/xml") }, - { assertThat(report.csv.outputLocationFilePath).endsWith("build/CSVISDABEST") }, - { assertThat(report.html.outputLocationFilePath).endsWith("build/html-reports/jacoco") }, - ) - } + project.tasks + .withType(AndroidJUnit5JacocoReport::class.java) + .map { it.reports } + .forEach { report -> + assertAll( + { + assertThat(report.xml.outputLocationFilePath) + .endsWith("build/other-jacoco-folder/xml") + }, + { assertThat(report.csv.outputLocationFilePath).endsWith("build/CSVISDABEST") }, + { + assertThat(report.html.outputLocationFilePath) + .endsWith("build/html-reports/jacoco") + }, + ) + } } @ValueSource(booleans = [true, false]) @@ -93,19 +100,20 @@ interface AgpJacocoBaseTests : AgpVariantAwareTests { } project.evaluate() - project.tasks.withType(AndroidJUnit5JacocoReport::class.java) - .map { it.reports } - .forEach { - assertAll( - { assertThat(it.xml.required.get() == required) }, - { assertThat(it.csv.required.get() == required) }, - { assertThat(it.html.required.get() == required) } - ) - } + project.tasks + .withType(AndroidJUnit5JacocoReport::class.java) + .map { it.reports } + .forEach { + assertAll( + { assertThat(it.xml.required.get() == required) }, + { assertThat(it.csv.required.get() == required) }, + { assertThat(it.html.required.get() == required) }, + ) + } } /* Private */ - private val ConfigurableReport.outputLocationFilePath get() = - outputLocationFile.asFile.orNull?.absolutePath + private val ConfigurableReport.outputLocationFilePath + get() = outputLocationFile.asFile.orNull?.absolutePath } diff --git a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AgpJacocoExclusionRuleTests.kt b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AgpJacocoExclusionRuleTests.kt index 767e9052..1e25682e 100644 --- a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AgpJacocoExclusionRuleTests.kt +++ b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AgpJacocoExclusionRuleTests.kt @@ -26,84 +26,92 @@ interface AgpJacocoExclusionRuleTests : AgpVariantAwareTests { @TestFactory fun `add exclusion rules`(): List { val project = createPreparedProject() - project.junitPlatform.jacocoOptions { - it.excludedClasses.add("Second*.class") - } + project.junitPlatform.jacocoOptions { it.excludedClasses.add("Second*.class") } project.evaluate() return listOf( - dynamicTest("honor the debug class exclusion rules") { - // Should be included: - // * FirstFile.class - // Should be excluded: - // * R.class (by default) - // * SecondFile.class (through rule) - val fileNames = project.tasks.get( - "${JACOCO_TASK_NAME}Debug") - .classDirectories!!.asFileTree.files - .map { it.name } - - assertThat(fileNames).apply { - contains("FirstFile.class") - doesNotContain("R.class") - doesNotContain("SecondFile.class") - } - }, - - dynamicTest("honor the release class exclusion rules") { - // Should be included: - // (nothing) - // Should be excluded: - // * R.class (by default) - // * FirstFile.class (other source set) - // * SecondFile.class (through rule) - val fileNames = project.tasks.get( - "${JACOCO_TASK_NAME}Release") - .classDirectories!!.asFileTree.files - .map { it.name } - - assertThat(fileNames).apply { - doesNotContain("R.class") - doesNotContain("FirstFile.class") - doesNotContain("SecondFile.class") - } + dynamicTest("honor the debug class exclusion rules") { + // Should be included: + // * FirstFile.class + // Should be excluded: + // * R.class (by default) + // * SecondFile.class (through rule) + val fileNames = + project.tasks + .get("${JACOCO_TASK_NAME}Debug") + .classDirectories!! + .asFileTree + .files + .map { it.name } + + assertThat(fileNames).apply { + contains("FirstFile.class") + doesNotContain("R.class") + doesNotContain("SecondFile.class") + } + }, + dynamicTest("honor the release class exclusion rules") { + // Should be included: + // (nothing) + // Should be excluded: + // * R.class (by default) + // * FirstFile.class (other source set) + // * SecondFile.class (through rule) + val fileNames = + project.tasks + .get("${JACOCO_TASK_NAME}Release") + .classDirectories!! + .asFileTree + .files + .map { it.name } + + assertThat(fileNames).apply { + doesNotContain("R.class") + doesNotContain("FirstFile.class") + doesNotContain("SecondFile.class") } + }, ) } @TestFactory - fun `replace exclusion rules`() = forEachBuildType( + fun `replace exclusion rules`() = + forEachBuildType( beforeBuild = { it.applyJacocoPlugin() }, beforeEvaluate = { project -> project.createFakeFiles() - project.junitPlatform.jacocoOptions { - it.excludedClasses.set(emptyList()) - } - } - ) { project, buildType -> - val name = "${JACOCO_TASK_NAME}${buildType.capitalized()}" - val fileNames = project.tasks.get(name) - .classDirectories!!.asFileTree.files - .map { it.name } - - // Should not exclude R.class any longer - assertThat(fileNames).contains("R.class") - } + project.junitPlatform.jacocoOptions { it.excludedClasses.set(emptyList()) } + }, + ) { project, buildType -> + val name = "${JACOCO_TASK_NAME}${buildType.capitalized()}" + val fileNames = + project.tasks + .get(name) + .classDirectories!! + .asFileTree + .files + .map { it.name } + + // Should not exclude R.class any longer + assertThat(fileNames).contains("R.class") + } } private fun Project.createFakeFiles() { - // Since the location of intermediate class files changed in different versions of the Android Gradle Plugin, + // Since the location of intermediate class files changed in different versions of the Android + // Gradle Plugin, // create each class file in multiple directories to remain compatible with all approaches. // First up, populate the empty folder structures, then add the files to them fakeFiles - .map { it.substringBeforeLast("/") } - .distinct() - .forEach { folder -> this.file(folder).mkdirs() } + .map { it.substringBeforeLast("/") } + .distinct() + .forEach { folder -> this.file(folder).mkdirs() } fakeFiles.forEach { file -> this.file(file).createNewFile() } } -private val fakeFiles = listOf( +private val fakeFiles = + listOf( // Debug classes "build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/R.class", "build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/FirstFile.class", @@ -126,5 +134,5 @@ private val fakeFiles = listOf( // Source files "src/main/java/OkFile.java", "src/main/java/AnnoyingFile.java", - "src/release/java/ReleaseOnlyFile.java" -) + "src/release/java/ReleaseOnlyFile.java", + ) diff --git a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AgpJacocoVariantTests.kt b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AgpJacocoVariantTests.kt index 4f99ee86..d2d6dd00 100644 --- a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AgpJacocoVariantTests.kt +++ b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AgpJacocoVariantTests.kt @@ -38,153 +38,134 @@ interface AgpJacocoVariantTests : AgpVariantAwareTests { } @TestFactory - fun `do not interfere if task generation is disabled`() = forEachBuildType( - beforeBuild = { - it.applyJacocoPlugin() - }, - beforeEvaluate = { - it.junitPlatform.jacocoOptions { - it.taskGenerationEnabled.set(false) - } + fun `do not interfere if task generation is disabled`() = + forEachBuildType( + beforeBuild = { it.applyJacocoPlugin() }, + beforeEvaluate = { + it.junitPlatform.jacocoOptions { it.taskGenerationEnabled.set(false) } + }, + ) { project, buildType -> + val name = jacocoVariantTaskName(buildType) + + assertWithMessage("do not create a child task for build type $buildType") + .that(project.tasks.findByName(name)) + .isNull() } - ) { project, buildType -> - val name = jacocoVariantTaskName(buildType) - - assertWithMessage("do not create a child task for build type $buildType") - .that(project.tasks.findByName(name)) - .isNull() - } @TestFactory - fun `do not interfere with custom Jacoco task if task generation is disabled`() = forEachBuildType( - beforeBuild = { - it.applyJacocoPlugin() - }, - beforeEvaluate = { - it.tasks.register("jacocoTestReport", JacocoReport::class.java) { task -> - task.group = "TEST MARKER" - } - it.junitPlatform.jacocoOptions { - it.taskGenerationEnabled.set(false) - } + fun `do not interfere with custom Jacoco task if task generation is disabled`() = + forEachBuildType( + beforeBuild = { it.applyJacocoPlugin() }, + beforeEvaluate = { + it.tasks.register("jacocoTestReport", JacocoReport::class.java) { task -> + task.group = "TEST MARKER" + } + it.junitPlatform.jacocoOptions { it.taskGenerationEnabled.set(false) } + }, + ) { project, buildType -> + val name = jacocoVariantTaskName(buildType) + + assertWithMessage("do not create a child task for build type $buildType") + .that(project.tasks.findByName(name)) + .isNull() + + assertWithMessage("do not overwrite the custom task") + .that(project.tasks.getByName("jacocoTestReport").group) + .isEqualTo("TEST MARKER") } - ) { project, buildType -> - val name = jacocoVariantTaskName(buildType) - assertWithMessage("do not create a child task for build type $buildType") - .that(project.tasks.findByName(name)) - .isNull() + @TestFactory + fun `hook in build-type-specific jacoco task to parent`() = + forEachBuildType(beforeBuild = { it.applyJacocoPlugin() }) { project, buildType -> + val name = jacocoVariantTaskName(buildType) - assertWithMessage("do not overwrite the custom task") - .that(project.tasks.getByName("jacocoTestReport").group) - .isEqualTo("TEST MARKER") - } + assertThat( + project.tasks.getByName(JACOCO_TASK_NAME).getDependentTaskNames().contains(name) + ) + } @TestFactory - fun `hook in build-type-specific jacoco task to parent`() = forEachBuildType( - beforeBuild = { it.applyJacocoPlugin() } - ) { project, buildType -> - val name = jacocoVariantTaskName(buildType) - - assertThat( - project.tasks.getByName(JACOCO_TASK_NAME) - .getDependentTaskNames() - .contains(name) - ) - } + fun `create variant-specific jacoco task`() = + forEachVariant(beforeBuild = { it.applyJacocoPlugin() }) { project, variant -> + val name = jacocoVariantTaskName(variant) + assertThat(project.tasks.findByName(name)).isNotNull() + } @TestFactory - fun `create variant-specific jacoco task`() = forEachVariant( - beforeBuild = { it.applyJacocoPlugin() } - ) { project, variant -> - val name = jacocoVariantTaskName(variant) - assertThat(project.tasks.findByName(name)).isNotNull() - } + fun `hook in variant-specific jacoco task to parent`() = + forEachVariant(beforeBuild = { it.applyJacocoPlugin() }) { project, variant -> + assertThat(project.tasks.getByName(JACOCO_TASK_NAME).getDependentTaskNames()) + .contains(jacocoVariantTaskName(variant)) + } @TestFactory - fun `hook in variant-specific jacoco task to parent`() = forEachVariant( - beforeBuild = { it.applyJacocoPlugin() } - ) { project, variant -> - assertThat( - project.tasks.getByName(JACOCO_TASK_NAME) - .getDependentTaskNames() - ) - .contains(jacocoVariantTaskName(variant)) - } + fun `jacoco task includes main-scoped source directories`() = + forEachBuildType(beforeBuild = { it.applyJacocoPlugin() }) { project, buildType -> + val name = jacocoVariantTaskName(buildType) + val sourceDirs = + project.tasks.get(name).sourceDirectories!!.map { + it.absolutePath + } - @TestFactory - fun `jacoco task includes main-scoped source directories`() = forEachBuildType( - beforeBuild = { it.applyJacocoPlugin() } - ) { project, buildType -> - val name = jacocoVariantTaskName(buildType) - val sourceDirs = project.tasks.get(name) - .sourceDirectories!! - .map { it.absolutePath } - - // Expected items: "src/main/java" & "src//java" - val mainDir = sourceDirs.find { it.endsWith("src/main/java") } - val typeDir = sourceDirs.find { it.endsWith("src/$buildType/java") } - - assertAll( - "Mismatch! Actual dirs: $sourceDirs", - { assertWithMessage("main").that(mainDir).isNotNull() }, - { assertWithMessage(buildType).that(typeDir).isNotNull() } - ) - } + // Expected items: "src/main/java" & "src//java" + val mainDir = sourceDirs.find { it.endsWith("src/main/java") } + val typeDir = sourceDirs.find { it.endsWith("src/$buildType/java") } + + assertAll( + "Mismatch! Actual dirs: $sourceDirs", + { assertWithMessage("main").that(mainDir).isNotNull() }, + { assertWithMessage(buildType).that(typeDir).isNotNull() }, + ) + } @TestFactory - fun `jacoco task does not include test-scoped source directories`() = forEachBuildType( - beforeBuild = { it.applyJacocoPlugin() } - ) { project, buildType -> - val name = jacocoVariantTaskName(buildType) - val sourceDirs = project.tasks.get(name) - .sourceDirectories!!.asPath - - // Expected omissions: "src/test/java" & "src/test/java" - assertAll( - "Mismatch! Actual dirs: $sourceDirs", - { assertThat(sourceDirs).doesNotContain("src/test/java") }, - { - assertThat(sourceDirs).doesNotContain("src/test${buildType.capitalized()}/java") - } - ) - } + fun `jacoco task does not include test-scoped source directories`() = + forEachBuildType(beforeBuild = { it.applyJacocoPlugin() }) { project, buildType -> + val name = jacocoVariantTaskName(buildType) + val sourceDirs = + project.tasks.get(name).sourceDirectories!!.asPath + + // Expected omissions: "src/test/java" & "src/test/java" + assertAll( + "Mismatch! Actual dirs: $sourceDirs", + { assertThat(sourceDirs).doesNotContain("src/test/java") }, + { assertThat(sourceDirs).doesNotContain("src/test${buildType.capitalized()}/java") }, + ) + } @TestFactory - fun `jacoco task does not include test-scoped class directories`() = forEachBuildType( - beforeBuild = { it.applyJacocoPlugin() } - ) { project, buildType -> - val name = jacocoVariantTaskName(buildType) - val classDirs = project.tasks.get(name) - .classDirectories!!.asPath - - // Expected omissions: "classes/test" - assertThat(classDirs).doesNotContain("classes/test") - } + fun `jacoco task does not include test-scoped class directories`() = + forEachBuildType(beforeBuild = { it.applyJacocoPlugin() }) { project, buildType -> + val name = jacocoVariantTaskName(buildType) + val classDirs = + project.tasks.get(name).classDirectories!!.asPath + + // Expected omissions: "classes/test" + assertThat(classDirs).doesNotContain("classes/test") + } @TestFactory fun `only generate jacoco task for debug builds`(): List { val project = createProject().applyJacocoPlugin().build() - project.junitPlatform.jacocoOptions { - it.onlyGenerateTasksForVariants.add("debug") - } + project.junitPlatform.jacocoOptions { it.onlyGenerateTasksForVariants.add("debug") } project.evaluate() return listOf( - JACOCO_TASK_NAME to true, - "${JACOCO_TASK_NAME}Debug" to true, - "${JACOCO_TASK_NAME}Release" to false - ).map { (taskName, shouldExist) -> - dynamicTest("$taskName task is${if (shouldExist) " " else " not"} generated") { - val task = project.tasks.findByName(taskName) - - if (shouldExist) { - assertThat(task).isNotNull() - } else { - assertThat(task).isNull() + JACOCO_TASK_NAME to true, + "${JACOCO_TASK_NAME}Debug" to true, + "${JACOCO_TASK_NAME}Release" to false, + ) + .map { (taskName, shouldExist) -> + dynamicTest("$taskName task is${if (shouldExist) " " else " not"} generated") { + val task = project.tasks.findByName(taskName) + + if (shouldExist) { + assertThat(task).isNotNull() + } else { + assertThat(task).isNull() + } } } - } } @TestFactory @@ -197,22 +178,23 @@ interface AgpJacocoVariantTests : AgpVariantAwareTests { project.evaluate() return listOf( - JACOCO_TASK_NAME to true, - "${JACOCO_TASK_NAME}PaidDebug" to true, - "${JACOCO_TASK_NAME}FreeRelease" to true, - "${JACOCO_TASK_NAME}PaidRelease" to false, - "${JACOCO_TASK_NAME}FreeDebug" to false - ).map { (taskName, shouldExist) -> - dynamicTest("$taskName task is${if (shouldExist) " " else " not"} generated") { - val task = project.tasks.findByName(taskName) - - if (shouldExist) { - assertThat(task).isNotNull() - } else { - assertThat(task).isNull() + JACOCO_TASK_NAME to true, + "${JACOCO_TASK_NAME}PaidDebug" to true, + "${JACOCO_TASK_NAME}FreeRelease" to true, + "${JACOCO_TASK_NAME}PaidRelease" to false, + "${JACOCO_TASK_NAME}FreeDebug" to false, + ) + .map { (taskName, shouldExist) -> + dynamicTest("$taskName task is${if (shouldExist) " " else " not"} generated") { + val task = project.tasks.findByName(taskName) + + if (shouldExist) { + assertThat(task).isNotNull() + } else { + assertThat(task).isNull() + } } } - } } @Test diff --git a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AgpProjectTests.kt b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AgpProjectTests.kt index 7a9d57ae..a26ebf18 100644 --- a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AgpProjectTests.kt +++ b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AgpProjectTests.kt @@ -3,9 +3,11 @@ package de.mannodermaus.gradle.plugins.junit5.plugin import de.mannodermaus.gradle.plugins.junit5.util.projects.PluginSpecProjectCreator.Builder /** - * Entry point for all supported versions of the Android Gradle Plugin. - * The parent class composes all relevant unit tests for each of the plugin types. + * Entry point for all supported versions of the Android Gradle Plugin. The parent class composes + * all relevant unit tests for each of the plugin types. */ class AndroidAppProjectTests : AbstractProjectTests(Builder::asAndroidApplication) + class AndroidLibraryProjectTests : AbstractProjectTests(Builder::asAndroidLibrary) + class AndroidDynamicFeatureProjectTests : AbstractProjectTests(Builder::asAndroidDynamicFeature) diff --git a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AgpTests.kt b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AgpTests.kt index 7da388ac..ba2268ec 100644 --- a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AgpTests.kt +++ b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AgpTests.kt @@ -16,12 +16,13 @@ data class FlavorSpec(val name: String, val dimension: String) interface AgpVariantAwareTests : AgpTests { fun defaultBuildTypes(): List + fun defaultProductFlavors(): List fun forEachBuildType( beforeBuild: ((PluginSpecProjectCreator.Builder) -> Unit) = {}, beforeEvaluate: (Project) -> Unit = {}, - testBody: (Project, String) -> Unit + testBody: (Project, String) -> Unit, ): List { val parentTestName = parentTestName() @@ -40,7 +41,7 @@ interface AgpVariantAwareTests : AgpTests { flavorCreator: () -> List = { defaultProductFlavors() }, beforeBuild: ((PluginSpecProjectCreator.Builder) -> Unit) = {}, beforeEvaluate: (Project) -> Unit = {}, - testBody: (Project, String) -> Unit + testBody: (Project, String) -> Unit, ): List { val parentTestName = parentTestName() val flavors = flavorCreator() @@ -61,7 +62,7 @@ interface AgpVariantAwareTests : AgpTests { flavorCreator: () -> List = { defaultProductFlavors() }, beforeBuild: ((PluginSpecProjectCreator.Builder) -> Unit) = {}, beforeEvaluate: (Project) -> Unit = {}, - testBody: (Project, String) -> Unit + testBody: (Project, String) -> Unit, ): List { val parentTestName = parentTestName() val flavors = flavorCreator() @@ -71,13 +72,15 @@ interface AgpVariantAwareTests : AgpTests { beforeEvaluate.invoke(project) project.evaluate() - return (defaultBuildTypes() * flavors).map { (buildType, flavor) -> - val variantName = "${flavor.name}${buildType.capitalized()}" + return (defaultBuildTypes() * flavors) + .map { (buildType, flavor) -> + val variantName = "${flavor.name}${buildType.capitalized()}" - DynamicTest.dynamicTest("$parentTestName for '$variantName'") { - testBody(project, variantName) + DynamicTest.dynamicTest("$parentTestName for '$variantName'") { + testBody(project, variantName) + } } - }.toList() + .toList() } // Helpers and extensions @@ -87,9 +90,7 @@ interface AgpVariantAwareTests : AgpTests { return Exception().stackTrace.getOrNull(5)?.methodName ?: "unknown test" } - fun Project.registerProductFlavors( - flavors: List = defaultProductFlavors() - ) { + fun Project.registerProductFlavors(flavors: List = defaultProductFlavors()) { val dimensions = flavors.map(FlavorSpec::dimension).distinct() with(project.android) { diff --git a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AgpVariantTests.kt b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AgpVariantTests.kt index 5bd785cd..e99d486e 100644 --- a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AgpVariantTests.kt +++ b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/AgpVariantTests.kt @@ -8,25 +8,29 @@ import org.junit.jupiter.api.TestFactory interface AgpVariantTests : AgpVariantAwareTests { @TestFactory - fun `does not create a build-type-specific jacoco task`() = forEachBuildType { project, buildType -> - val buildTypeName = buildType.capitalized() - val name = "jacocoTestReport$buildTypeName" + fun `does not create a build-type-specific jacoco task`() = + forEachBuildType { project, buildType -> + val buildTypeName = buildType.capitalized() + val name = "jacocoTestReport$buildTypeName" - assertThat(project.tasks.findByName(name)).isNull() - } + assertThat(project.tasks.findByName(name)).isNull() + } @TestFactory - fun `add build-type-specific filter DSL to the extension`() = forEachBuildType { project, buildType -> - assertThat(project.junitPlatform.filters(buildType)).isNotNull() - } + fun `add build-type-specific filter DSL to the extension`() = + forEachBuildType { project, buildType -> + assertThat(project.junitPlatform.filters(buildType)).isNotNull() + } @TestFactory - fun `add a flavor-specific filter DSL to the extension`() = forEachProductFlavor { project, flavor -> - assertThat(project.junitPlatform.filters(flavor)).isNotNull() - } + fun `add a flavor-specific filter DSL to the extension`() = + forEachProductFlavor { project, flavor -> + assertThat(project.junitPlatform.filters(flavor)).isNotNull() + } @TestFactory - fun `add a variant-specific filter DSL to the extension`() = forEachVariant { project, variant -> - assertThat(project.junitPlatform.filters(variant)).isNotNull() - } + fun `add a variant-specific filter DSL to the extension`() = + forEachVariant { project, variant -> + assertThat(project.junitPlatform.filters(variant)).isNotNull() + } } diff --git a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/InstrumentationSupportTests.kt b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/InstrumentationSupportTests.kt index 8c3df8ca..9ef44084 100644 --- a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/InstrumentationSupportTests.kt +++ b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/InstrumentationSupportTests.kt @@ -1,7 +1,6 @@ package de.mannodermaus.gradle.plugins.junit5.plugin import com.google.common.truth.Truth.assertThat -import de.mannodermaus.Libraries import de.mannodermaus.Libraries.Instrumentation import de.mannodermaus.Libraries.JUnit import de.mannodermaus.Libraries.JUnit.JUnit5 @@ -20,18 +19,14 @@ import org.junit.jupiter.params.provider.EnumSource internal class InstrumentationSupportTests { - @RegisterExtension - @JvmField - val projectExtension = TestProjectProviderExtension() + @RegisterExtension @JvmField val projectExtension = TestProjectProviderExtension() private lateinit var project: Project @BeforeEach fun beforeEach() { - project = projectExtension.newProject() - .asAndroidApplication() - .applyJUnit5Plugin(true) - .build() + project = + projectExtension.newProject().asAndroidApplication().applyJUnit5Plugin(true).build() } /* RunnerBuilder */ @@ -41,17 +36,22 @@ internal class InstrumentationSupportTests { project.addJUnit(JUnit5, "androidTest") project.evaluate() - assertThat(project.android.defaultConfig.testInstrumentationRunnerArguments["runnerBuilder"]) + assertThat( + project.android.defaultConfig.testInstrumentationRunnerArguments["runnerBuilder"] + ) .isEqualTo(ANDROID_JUNIT5_RUNNER_BUILDER_CLASS) } @Test fun `maintain any existing RunnerBuilder`() { project.addJUnit(JUnit5, "androidTest") - project.android.defaultConfig.testInstrumentationRunnerArguments["runnerBuilder"] = "something.else" + project.android.defaultConfig.testInstrumentationRunnerArguments["runnerBuilder"] = + "something.else" project.evaluate() - assertThat(project.android.defaultConfig.testInstrumentationRunnerArguments["runnerBuilder"]) + assertThat( + project.android.defaultConfig.testInstrumentationRunnerArguments["runnerBuilder"] + ) .isEqualTo("something.else,$ANDROID_JUNIT5_RUNNER_BUILDER_CLASS") } @@ -61,14 +61,20 @@ internal class InstrumentationSupportTests { project.junitPlatform.instrumentationTests.enabled.set(false) project.evaluate() - assertThat(project.android.defaultConfig.testInstrumentationRunnerArguments["runnerBuilder"]).isNull() + assertThat( + project.android.defaultConfig.testInstrumentationRunnerArguments["runnerBuilder"] + ) + .isNull() } @Test fun `do not add the RunnerBuilder when Jupiter is not added`() { project.evaluate() - assertThat(project.android.defaultConfig.testInstrumentationRunnerArguments["runnerBuilder"]).isNull() + assertThat( + project.android.defaultConfig.testInstrumentationRunnerArguments["runnerBuilder"] + ) + .isNull() } /* Configuration parameters */ @@ -82,7 +88,10 @@ internal class InstrumentationSupportTests { } project.evaluate() - assertThat(project.android.defaultConfig.testInstrumentationRunnerArguments["configurationParameters"]) + assertThat( + project.android.defaultConfig.testInstrumentationRunnerArguments[ + "configurationParameters"] + ) .isEqualTo("my.parameter1=true,my.parameter2=1234") } @@ -96,7 +105,10 @@ internal class InstrumentationSupportTests { } project.evaluate() - assertThat(project.android.defaultConfig.testInstrumentationRunnerArguments["configurationParameters"]) + assertThat( + project.android.defaultConfig.testInstrumentationRunnerArguments[ + "configurationParameters"] + ) .isNull() } @@ -109,10 +121,16 @@ internal class InstrumentationSupportTests { project.evaluate() assertThat(project).configuration("testImplementation").hasDependency(coreLibrary(junit)) - assertThat(project).configuration("testRuntimeOnly").doesNotHaveDependency(runnerLibrary(junit)) - - assertThat(project).configuration("testImplementation").doesNotHaveDependency(extensionsLibrary(junit)) - assertThat(project).configuration("testImplementation").doesNotHaveDependency(composeLibrary(junit)) + assertThat(project) + .configuration("testRuntimeOnly") + .doesNotHaveDependency(runnerLibrary(junit)) + + assertThat(project) + .configuration("testImplementation") + .doesNotHaveDependency(extensionsLibrary(junit)) + assertThat(project) + .configuration("testImplementation") + .doesNotHaveDependency(composeLibrary(junit)) } @EnumSource(JUnit::class) @@ -121,11 +139,19 @@ internal class InstrumentationSupportTests { project.addJUnit(junit, "androidTest") project.evaluate() - assertThat(project).configuration("androidTestImplementation").hasDependency(coreLibrary(junit)) - assertThat(project).configuration("androidTestRuntimeOnly").hasDependency(runnerLibrary(junit)) - - assertThat(project).configuration("androidTestImplementation").doesNotHaveDependency(extensionsLibrary(junit)) - assertThat(project).configuration("androidTestImplementation").doesNotHaveDependency(composeLibrary(junit)) + assertThat(project) + .configuration("androidTestImplementation") + .hasDependency(coreLibrary(junit)) + assertThat(project) + .configuration("androidTestRuntimeOnly") + .hasDependency(runnerLibrary(junit)) + + assertThat(project) + .configuration("androidTestImplementation") + .doesNotHaveDependency(extensionsLibrary(junit)) + assertThat(project) + .configuration("androidTestImplementation") + .doesNotHaveDependency(composeLibrary(junit)) } @EnumSource(JUnit::class) @@ -135,8 +161,12 @@ internal class InstrumentationSupportTests { project.junitPlatform.instrumentationTests.version.set("1.3.3.7") project.evaluate() - assertThat(project).configuration("androidTestImplementation").hasDependency(coreLibrary(junit, "1.3.3.7")) - assertThat(project).configuration("androidTestRuntimeOnly").hasDependency(runnerLibrary(junit, "1.3.3.7")) + assertThat(project) + .configuration("androidTestImplementation") + .hasDependency(coreLibrary(junit, "1.3.3.7")) + assertThat(project) + .configuration("androidTestRuntimeOnly") + .hasDependency(runnerLibrary(junit, "1.3.3.7")) } @EnumSource(JUnit::class) @@ -149,8 +179,12 @@ internal class InstrumentationSupportTests { project.dependencies.add("androidTestRuntimeOnly", addedRunner) project.evaluate() - assertThat(project).configuration("androidTestImplementation").hasDependency(coreLibrary(junit, "0.1.3.3.7")) - assertThat(project).configuration("androidTestRuntimeOnly").hasDependency(runnerLibrary(junit, "0.1.3.3.7")) + assertThat(project) + .configuration("androidTestImplementation") + .hasDependency(coreLibrary(junit, "0.1.3.3.7")) + assertThat(project) + .configuration("androidTestRuntimeOnly") + .hasDependency(runnerLibrary(junit, "0.1.3.3.7")) } @EnumSource(JUnit::class) @@ -160,8 +194,12 @@ internal class InstrumentationSupportTests { project.junitPlatform.instrumentationTests.enabled.set(false) project.evaluate() - assertThat(project).configuration("androidTestImplementation").doesNotHaveDependency(coreLibrary(junit)) - assertThat(project).configuration("androidTestRuntimeOnly").doesNotHaveDependency(runnerLibrary(junit)) + assertThat(project) + .configuration("androidTestImplementation") + .doesNotHaveDependency(coreLibrary(junit)) + assertThat(project) + .configuration("androidTestRuntimeOnly") + .doesNotHaveDependency(runnerLibrary(junit)) } @EnumSource(JUnit::class) @@ -169,19 +207,31 @@ internal class InstrumentationSupportTests { fun `do not add the dependencies when Jupiter is not added`(junit: JUnit) { project.evaluate() - assertThat(project).configuration("androidTestImplementation").doesNotHaveDependency(coreLibrary(junit)) - assertThat(project).configuration("androidTestRuntimeOnly").doesNotHaveDependency(runnerLibrary(junit)) + assertThat(project) + .configuration("androidTestImplementation") + .doesNotHaveDependency(coreLibrary(junit)) + assertThat(project) + .configuration("androidTestRuntimeOnly") + .doesNotHaveDependency(runnerLibrary(junit)) } @EnumSource(JUnit::class) @ParameterizedTest - fun `do not add the dependencies when Jupiter is not added, even if extension is configured to be added`(junit: JUnit) { + fun `do not add the dependencies when Jupiter is not added, even if extension is configured to be added`( + junit: JUnit + ) { project.junitPlatform.instrumentationTests.includeExtensions.set(true) project.evaluate() - assertThat(project).configuration("androidTestImplementation").doesNotHaveDependency(coreLibrary(junit)) - assertThat(project).configuration("androidTestImplementation").doesNotHaveDependency(extensionsLibrary(junit)) - assertThat(project).configuration("androidTestRuntimeOnly").doesNotHaveDependency(runnerLibrary(junit)) + assertThat(project) + .configuration("androidTestImplementation") + .doesNotHaveDependency(coreLibrary(junit)) + assertThat(project) + .configuration("androidTestImplementation") + .doesNotHaveDependency(extensionsLibrary(junit)) + assertThat(project) + .configuration("androidTestRuntimeOnly") + .doesNotHaveDependency(runnerLibrary(junit)) } @EnumSource(JUnit::class) @@ -191,9 +241,15 @@ internal class InstrumentationSupportTests { project.junitPlatform.instrumentationTests.includeExtensions.set(true) project.evaluate() - assertThat(project).configuration("androidTestImplementation").hasDependency(coreLibrary(junit)) - assertThat(project).configuration("androidTestImplementation").hasDependency(extensionsLibrary(junit)) - assertThat(project).configuration("androidTestRuntimeOnly").hasDependency(runnerLibrary(junit)) + assertThat(project) + .configuration("androidTestImplementation") + .hasDependency(coreLibrary(junit)) + assertThat(project) + .configuration("androidTestImplementation") + .hasDependency(extensionsLibrary(junit)) + assertThat(project) + .configuration("androidTestRuntimeOnly") + .hasDependency(runnerLibrary(junit)) } @EnumSource(JUnit::class) @@ -207,9 +263,15 @@ internal class InstrumentationSupportTests { assertThat(project).configuration("testImplementation").hasDependency(coreLibrary(junit)) assertThat(project).configuration("testImplementation").hasDependency(composeLibrary(junit)) - assertThat(project).configuration("androidTestImplementation").hasDependency(coreLibrary(junit)) - assertThat(project).configuration("androidTestImplementation").hasDependency(composeLibrary(junit)) - assertThat(project).configuration("androidTestRuntimeOnly").hasDependency(runnerLibrary(junit)) + assertThat(project) + .configuration("androidTestImplementation") + .hasDependency(coreLibrary(junit)) + assertThat(project) + .configuration("androidTestImplementation") + .hasDependency(composeLibrary(junit)) + assertThat(project) + .configuration("androidTestRuntimeOnly") + .hasDependency(runnerLibrary(junit)) } @EnumSource(JUnit::class) @@ -224,11 +286,21 @@ internal class InstrumentationSupportTests { assertThat(project).configuration("testImplementation").hasDependency(coreLibrary(junit)) assertThat(project).configuration("testImplementation").hasDependency(composeLibrary(junit)) - assertThat(project).configuration("testImplementation").hasDependency(extensionsLibrary(junit)) - assertThat(project).configuration("androidTestImplementation").hasDependency(coreLibrary(junit)) - assertThat(project).configuration("androidTestImplementation").hasDependency(composeLibrary(junit)) - assertThat(project).configuration("androidTestImplementation").hasDependency(extensionsLibrary(junit)) - assertThat(project).configuration("androidTestRuntimeOnly").hasDependency(runnerLibrary(junit)) + assertThat(project) + .configuration("testImplementation") + .hasDependency(extensionsLibrary(junit)) + assertThat(project) + .configuration("androidTestImplementation") + .hasDependency(coreLibrary(junit)) + assertThat(project) + .configuration("androidTestImplementation") + .hasDependency(composeLibrary(junit)) + assertThat(project) + .configuration("androidTestImplementation") + .hasDependency(extensionsLibrary(junit)) + assertThat(project) + .configuration("androidTestRuntimeOnly") + .hasDependency(runnerLibrary(junit)) } @Test @@ -265,5 +337,6 @@ internal class InstrumentationSupportTests { private fun runnerLibrary(junit: JUnit, withVersion: String? = Instrumentation.version) = library(Instrumentation.runner, junit, withVersion) - private fun library(artifactId: String, junit: JUnit, version: String?) = junit.artifact(artifactId, version) + private fun library(artifactId: String, junit: JUnit, version: String?) = + junit.artifact(artifactId, version) } diff --git a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/TestProjectProviderExtension.kt b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/TestProjectProviderExtension.kt index fce0f7c3..cb468380 100644 --- a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/TestProjectProviderExtension.kt +++ b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/TestProjectProviderExtension.kt @@ -8,9 +8,8 @@ import org.junit.jupiter.api.extension.BeforeEachCallback import org.junit.jupiter.api.extension.ExtensionContext /** - * A Junit 5 extension to provide the handle - * to a Gradle project structure for testing. - * Cleans up automatically after each test. + * A Junit 5 extension to provide the handle to a Gradle project structure for testing. Cleans up + * automatically after each test. */ class TestProjectProviderExtension : BeforeEachCallback, AfterEachCallback { diff --git a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/WrongPluginUsageTests.kt b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/WrongPluginUsageTests.kt index 2c2e8a1b..29e7422e 100644 --- a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/WrongPluginUsageTests.kt +++ b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/plugin/WrongPluginUsageTests.kt @@ -8,16 +8,17 @@ import org.junit.jupiter.api.extension.RegisterExtension class WrongPluginUsageTests { - @RegisterExtension - @JvmField - val projectExtension = TestProjectProviderExtension() + @RegisterExtension @JvmField val projectExtension = TestProjectProviderExtension() @Test fun `not applying any supported Android plugin`() { - val exception = assertThrows { - projectExtension.newProject().buildAndEvaluate() - } + val exception = + assertThrows { + projectExtension.newProject().buildAndEvaluate() + } assertThat(exception.cause?.message) - .contains("An Android plugin must be applied in order for android-junit5 to work correctly!") + .contains( + "An Android plugin must be applied in order for android-junit5 to work correctly!" + ) } } diff --git a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/tasks/AndroidJUnit5WriteFiltersTests.kt b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/tasks/AndroidJUnit5WriteFiltersTests.kt index d83cec29..36c117cb 100644 --- a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/tasks/AndroidJUnit5WriteFiltersTests.kt +++ b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/tasks/AndroidJUnit5WriteFiltersTests.kt @@ -5,30 +5,30 @@ import de.mannodermaus.gradle.plugins.junit5.internal.config.INSTRUMENTATION_FIL import de.mannodermaus.gradle.plugins.junit5.plugin.TestProjectProviderExtension import de.mannodermaus.gradle.plugins.junit5.util.assertAll import de.mannodermaus.gradle.plugins.junit5.util.evaluate +import java.io.File +import java.nio.file.Paths +import kotlin.io.path.readLines import org.gradle.api.Project import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.RegisterExtension -import java.io.File -import java.nio.file.Paths -import kotlin.io.path.readLines class AndroidJUnit5WriteFiltersTests { - @RegisterExtension - @JvmField - val projectExtension = TestProjectProviderExtension() + @RegisterExtension @JvmField val projectExtension = TestProjectProviderExtension() private lateinit var project: Project @BeforeEach fun beforeEach() { - project = projectExtension.newProject() - .asAndroidApplication() - .applyJUnit5Plugin(true) { junitPlatform -> - junitPlatform.filters().includeTags("included") - junitPlatform.filters().excludeTags("excluded", "another-group") - } - .build() + project = + projectExtension + .newProject() + .asAndroidApplication() + .applyJUnit5Plugin(true) { junitPlatform -> + junitPlatform.filters().includeTags("included") + junitPlatform.filters().excludeTags("excluded", "another-group") + } + .build() project.evaluate() } @@ -60,17 +60,14 @@ class AndroidJUnit5WriteFiltersTests { val file = Paths.get(output.absolutePath, "raw", INSTRUMENTATION_FILTER_RES_FILE_NAME) val content = file.readLines() - assertThat(content).containsExactly( - "-t included", - "-T excluded", - "-T another-group", - ) + assertThat(content).containsExactly("-t included", "-T excluded", "-T another-group") } /* Private */ private fun Project.runTaskAndGetOutputFolder(): File { - val task = project.tasks.getByName("writeFiltersDebugAndroidTest") as AndroidJUnit5WriteFilters + val task = + project.tasks.getByName("writeFiltersDebugAndroidTest") as AndroidJUnit5WriteFilters task.execute() return requireNotNull(task.outputFolder.get().asFile) } diff --git a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/GradleTruth.kt b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/GradleTruth.kt index b51bfce7..4da5bbc1 100644 --- a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/GradleTruth.kt +++ b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/GradleTruth.kt @@ -11,33 +11,26 @@ import org.gradle.api.artifacts.Configuration /* Methods */ -fun assertThat(actual: Project): ProjectSubject = - Truth.assertAbout(::ProjectSubject).that(actual) +fun assertThat(actual: Project): ProjectSubject = Truth.assertAbout(::ProjectSubject).that(actual) /* Types */ -class ProjectSubject( - metadata: FailureMetadata, - private val actual: Project?, -) : Subject(metadata, actual) { +class ProjectSubject(metadata: FailureMetadata, private val actual: Project?) : + Subject(metadata, actual) { - fun configuration(name: String): ConfigurationSubject = check("configuration()") - .about(::ConfigurationSubject) - .that(actual?.configurations?.getByName(name)) + fun configuration(name: String): ConfigurationSubject = + check("configuration()") + .about(::ConfigurationSubject) + .that(actual?.configurations?.getByName(name)) - fun task(name: String): TaskSubject = check("task()") - .about(::TaskSubject) - .that(actual?.tasks?.findByName(name)) + fun task(name: String): TaskSubject = + check("task()").about(::TaskSubject).that(actual?.tasks?.findByName(name)) } -class ConfigurationSubject( - metadata: FailureMetadata, - private val actual: Configuration?, -) : Subject(metadata, actual) { +class ConfigurationSubject(metadata: FailureMetadata, private val actual: Configuration?) : + Subject(metadata, actual) { private val dependencyNames by lazy { - actual?.dependencies - ?.map { "${it.group}:${it.name}:${it.version}" } - .orEmpty() + actual?.dependencies?.map { "${it.group}:${it.name}:${it.version}" }.orEmpty() } fun hasDependency(notation: String) { @@ -55,28 +48,28 @@ class ConfigurationSubject( // include it in the check. Otherwise, check for the existence // of _any_ version for the dependency in question val notationIncludesVersion = notation.count { it == ':' } > 1 - val hasMatch = if (notationIncludesVersion) { - notation in dependencyNames - } else { - dependencyNames.any { it.startsWith("$notation:") } - } - - val messagePrefix = if (expectExists) { - "Expected to have a dependency on '$notation' in configuration '${actual?.name}', but did not." - } else { - "Expected not to have a dependency on '$notation' in configuration '${actual?.name}', but did." - } - - assertWithMessage( - "$messagePrefix\nDependencies in this configuration: $dependencyNames" - ).that(hasMatch).isEqualTo(expectExists) + val hasMatch = + if (notationIncludesVersion) { + notation in dependencyNames + } else { + dependencyNames.any { it.startsWith("$notation:") } + } + + val messagePrefix = + if (expectExists) { + "Expected to have a dependency on '$notation' in configuration '${actual?.name}', but did not." + } else { + "Expected not to have a dependency on '$notation' in configuration '${actual?.name}', but did." + } + + assertWithMessage("$messagePrefix\nDependencies in this configuration: $dependencyNames") + .that(hasMatch) + .isEqualTo(expectExists) } } -class TaskSubject( - metadata: FailureMetadata, - private val actual: Task?, -) : Subject(metadata, actual) { +class TaskSubject(metadata: FailureMetadata, private val actual: Task?) : + Subject(metadata, actual) { fun exists() { assertThat(actual).isNotNull() } diff --git a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/TestEnvironment.kt b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/TestEnvironment.kt index 8be77de6..4910fef9 100644 --- a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/TestEnvironment.kt +++ b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/TestEnvironment.kt @@ -3,12 +3,12 @@ package de.mannodermaus.gradle.plugins.junit5.util import java.io.File import java.util.Properties - private const val ANDROID_SDK_FILE_NAME = "local.properties" private const val ANDROID_SDK_PROP_NAME = "sdk.dir" private const val ANDROID_HOME_ENVVAR_NAME = "ANDROID_HOME" -private const val ENVIRONMENT_RESOURCE_NAME = "/de/mannodermaus/gradle/plugins/junit5/testenv.properties" +private const val ENVIRONMENT_RESOURCE_NAME = + "/de/mannodermaus/gradle/plugins/junit5/testenv.properties" private const val COMPILE_SDK_PROP_NAME = "COMPILE_SDK_VERSION" private const val MIN_SDK_PROP_NAME = "MIN_SDK_VERSION" private const val TARGET_SDK_PROP_NAME = "TARGET_SDK_VERSION" @@ -21,11 +21,11 @@ private const val USER_DIR_PROP_NAME = "user.dir" private const val BUILD_SRC_FOLDER_NAME = "buildSrc" /** - * Encapsulates environment properties related to running - * Unit Tests that interface with an Android SDK installation. + * Encapsulates environment properties related to running Unit Tests that interface with an Android + * SDK installation. * - * Typically, test cases don't need to instantiate this themselves, - * as its creation is hooked into the lifecycle of the enclosing test class. + * Typically, test cases don't need to instantiate this themselves, as its creation is hooked into + * the lifecycle of the enclosing test class. */ class TestEnvironment { val androidSdkFolder = loadAndroidSdkFolder() @@ -51,27 +51,28 @@ class TestEnvironment { // Each entry in this string is separated by semicolon. // Within each entry, the pipe ("|") divides it into four properties val agpVersionsString = envProps.getProperty(AGP_VERSIONS_PROP_NAME) - supportedAgpVersions = agpVersionsString.split(";") - .map { entry -> entry.split("|") } - .map { values -> - TestedAgp( - shortVersion = values[0], - version = values[1], - requiresGradle = values[2], - requiresCompileSdk = values[3].toIntOrNull() - ) - } + supportedAgpVersions = + agpVersionsString + .split(";") + .map { entry -> entry.split("|") } + .map { values -> + TestedAgp( + shortVersion = values[0], + version = values[1], + requiresGradle = values[2], + requiresCompileSdk = values[3].toIntOrNull(), + ) + } // Each entry in this string is separated by semicolon. val junitVersionsString = envProps.getProperty(JUNIT_VERSIONS_PROP_NAME) - supportedJUnitVersions = junitVersionsString.split(";") - .map { entry -> entry.split("|") } - .map { values -> - TestedJUnit( - majorVersion = values[0].toInt(), - fullVersion = values[1] - ) - } + supportedJUnitVersions = + junitVersionsString + .split(";") + .map { entry -> entry.split("|") } + .map { values -> + TestedJUnit(majorVersion = values[0].toInt(), fullVersion = values[1]) + } } } @@ -83,8 +84,8 @@ private fun loadAndroidSdkFolder(): File { ?: loadAndroidSdkFromEnvVar() ?: throw AssertionError( "Android SDK couldn't be found. Either local.properties file in project root is missing, " + - "it doesn't include the required 'sdk.dir' statement, " + - "or there is no ANDROID_HOME environment variable!" + "it doesn't include the required 'sdk.dir' statement, " + + "or there is no ANDROID_HOME environment variable!" ) } @@ -98,9 +99,11 @@ private fun loadAndroidSdkFromProject(): File? { while (file != null) { val localPropsFile = File(file, ANDROID_SDK_FILE_NAME) if (localPropsFile.exists()) { - val sdkFolderProp = localPropsFile.readLines() - .find { it.startsWith(ANDROID_SDK_PROP_NAME) } - ?.run { this.substring(this.indexOf('=') + 1).trim() } + val sdkFolderProp = + localPropsFile + .readLines() + .find { it.startsWith(ANDROID_SDK_PROP_NAME) } + ?.run { this.substring(this.indexOf('=') + 1).trim() } if (sdkFolderProp != null) { // Found match; abort @@ -121,12 +124,11 @@ private fun loadAndroidSdkFromProject(): File? { return null } -private fun loadAndroidSdkFromEnvVar() = - System.getenv(ANDROID_HOME_ENVVAR_NAME)?.run { File(this) } +private fun loadAndroidSdkFromEnvVar() = System.getenv(ANDROID_HOME_ENVVAR_NAME)?.run { File(this) } private fun loadAndroidEnvironment() = Properties().apply { - TestEnvironment::class.java.getResourceAsStream(ENVIRONMENT_RESOURCE_NAME)!! - .reader() - .use { this.load(it) } + TestEnvironment::class.java.getResourceAsStream(ENVIRONMENT_RESOURCE_NAME)!!.reader().use { + this.load(it) + } } diff --git a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/TestExtensions.kt b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/TestExtensions.kt index c6fefaf4..ed09d735 100644 --- a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/TestExtensions.kt +++ b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/TestExtensions.kt @@ -1,5 +1,9 @@ package de.mannodermaus.gradle.plugins.junit5.util +import java.io.File +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.internal.project.ProjectInternal @@ -13,14 +17,8 @@ import org.gradle.testkit.runner.GradleRunner import org.gradle.testkit.runner.internal.PluginUnderTestMetadataReading import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.function.Executable -import java.io.File -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.Paths -/** - * @author Michael Clausen - encodeering@gmail.com - */ +/** @author Michael Clausen - encodeering@gmail.com */ inline fun throws(block: () -> Unit): T { var ex: Throwable? = null var thrown = false @@ -32,10 +30,11 @@ inline fun throws(block: () -> Unit): T { ex = e matches = T::class.isInstance(e) thrown = true - } finally { - if (!matches && ex != null) throw AssertionError( - "block should have thrown a ${T::class.simpleName}, but threw a ${ex.javaClass.simpleName}") + if (!matches && ex != null) + throw AssertionError( + "block should have thrown a ${T::class.simpleName}, but threw a ${ex.javaClass.simpleName}" + ) if (!thrown) throw AssertionError("block should have thrown a ${T::class.simpleName}") } @@ -59,27 +58,28 @@ fun Project.evaluate() { fun Project.applyPlugin(pluginId: String) = apply { it.plugin(pluginId) } @Suppress("UNCHECKED_CAST") -fun TaskContainer.get(name: String): T = - this.getByName(name) as T +fun TaskContainer.get(name: String): T = this.getByName(name) as T fun Task.getDependentTaskNames(): List = - this.dependsOn.map { dependent -> - when (dependent) { - is Task -> dependent.name - is TaskProvider<*> -> dependent.name - else -> throw IllegalArgumentException("don't know how to extract task name from: $dependent") - } + this.dependsOn.map { dependent -> + when (dependent) { + is Task -> dependent.name + is TaskProvider<*> -> dependent.name + else -> + throw IllegalArgumentException( + "don't know how to extract task name from: $dependent" + ) } + } fun File.newFile(filePath: String, separator: String = "/"): File { - val path = Paths.get(this.toString(), - *filePath.splitToArray(delimiter = separator)) + val path = Paths.get(this.toString(), *filePath.splitToArray(delimiter = separator)) path.parent.mkdirs() return path.toFile() } fun String.splitToArray(delimiter: String = "/"): Array = - this.split(delimiter).toTypedArray() + this.split(delimiter).toTypedArray() fun Path.mkdirs() = Files.createDirectories(this) @@ -88,9 +88,8 @@ fun Path.newFile(path: String) = this.resolve(path).toFile() val Test.junitPlatformOptions: JUnitPlatformOptions get() = (this.testFramework as JUnitPlatformTestFramework).options -fun List.splitClasspath() = this - .map { it.absolutePath.replace("\\", "\\\\") } - .joinToString(", ") { "'$it'" } +fun List.splitClasspath() = + this.map { it.absolutePath.replace("\\", "\\\\") }.joinToString(", ") { "'$it'" } fun GradleRunner.withPrunedPluginClasspath(agpVersion: TestedAgp) = also { val fileKey = agpVersion.fileKey @@ -102,8 +101,8 @@ fun GradleRunner.withPrunedPluginClasspath(agpVersion: TestedAgp) = also { /* Operators */ /** - * Produces the [cartesian product](http://en.wikipedia.org/wiki/Cartesian_product#n-ary_product) as a sequence of ordered pairs of elements lazily obtained - * from two [[Iterable]] instances + * Produces the [cartesian product](http://en.wikipedia.org/wiki/Cartesian_product#n-ary_product) as + * a sequence of ordered pairs of elements lazily obtained from two [[Iterable]] instances */ operator fun Iterable.times(other: Iterable): Sequence> { val first = iterator() @@ -114,7 +113,8 @@ operator fun Iterable.times(other: Iterable): Sequence< if (a == null && first.hasNext()) a = first.next() if (second.hasNext()) return Pair(a!!, second.next()) if (first.hasNext()) { - a = first.next(); second = other.iterator() + a = first.next() + second = other.iterator() return Pair(a, second.next()) } return null @@ -126,8 +126,7 @@ operator fun Iterable.times(other: Iterable): Sequence< fun BuildResult.prettyPrint() { // Indent every line to separate it from 'actual' Gradle output val prefix = "[BuildResult-${hashCode()}] " - val fixedOutput = this.output.lines() - .joinToString("\n") { "$prefix$it" } + val fixedOutput = this.output.lines().joinToString("\n") { "$prefix$it" } println(fixedOutput) } diff --git a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/TestKitTruth.kt b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/TestKitTruth.kt index 33bb5310..14ec9fa5 100644 --- a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/TestKitTruth.kt +++ b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/TestKitTruth.kt @@ -17,34 +17,25 @@ fun assertThat(actual: BuildResult): BuildResultSubject = /* Types */ -class BuildResultSubject( - metadata: FailureMetadata, - private val actual: BuildResult? -) : Subject(metadata, actual) { - - fun task(name: String): BuildTaskSubject = check("task()") - .about(::BuildTaskSubject) - .that(actual?.task(name)) - - fun output(): BuildResultOutputSubject = check("output()") - .about(::BuildResultOutputSubject) - .that(actual?.output) +class BuildResultSubject(metadata: FailureMetadata, private val actual: BuildResult?) : + Subject(metadata, actual) { + + fun task(name: String): BuildTaskSubject = + check("task()").about(::BuildTaskSubject).that(actual?.task(name)) + + fun output(): BuildResultOutputSubject = + check("output()").about(::BuildResultOutputSubject).that(actual?.output) } -class BuildTaskSubject( - metadata: FailureMetadata, - private val actual: BuildTask? -) : Subject(metadata, actual) { +class BuildTaskSubject(metadata: FailureMetadata, private val actual: BuildTask?) : + Subject(metadata, actual) { - fun hasOutcome(expected: TaskOutcome) = check("hasOutcome()") - .that(actual?.outcome) - .isEqualTo(expected) + fun hasOutcome(expected: TaskOutcome) = + check("hasOutcome()").that(actual?.outcome).isEqualTo(expected) } -class BuildResultOutputSubject( - metadata: FailureMetadata, - private val actual: String? -) : StringSubject(metadata, actual) { +class BuildResultOutputSubject(metadata: FailureMetadata, private val actual: String?) : + StringSubject(metadata, actual) { fun ofTask(name: String): BuildTaskOutputSubject { requireNotNull(actual) @@ -60,16 +51,12 @@ class BuildResultOutputSubject( } val strippedOutput = actual.substring(startIndex, endIndex) - return check("ofTask()") - .about(::BuildTaskOutputSubject) - .that(strippedOutput) + return check("ofTask()").about(::BuildTaskOutputSubject).that(strippedOutput) } } -class BuildTaskOutputSubject( - metadata: FailureMetadata, - private val actual: String? -) : StringSubject(metadata, actual) { +class BuildTaskOutputSubject(metadata: FailureMetadata, private val actual: String?) : + StringSubject(metadata, actual) { fun executedTestCount(): IntegerSubject { requireNotNull(actual) diff --git a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/TestedAgp.kt b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/TestedAgp.kt index 80184b88..dfb72d4b 100644 --- a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/TestedAgp.kt +++ b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/TestedAgp.kt @@ -1,10 +1,10 @@ package de.mannodermaus.gradle.plugins.junit5.util data class TestedAgp( - val shortVersion: String, - val version: String, - val requiresGradle: String, - val requiresCompileSdk: Int? + val shortVersion: String, + val version: String, + val requiresGradle: String, + val requiresCompileSdk: Int?, ) { - val fileKey: String = "agp${shortVersion.replace(".", "")}x" + val fileKey: String = "agp${shortVersion.replace(".", "")}x" } diff --git a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/projects/BuildScriptTemplateProcessor.kt b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/projects/BuildScriptTemplateProcessor.kt index 1cd3a31e..b5cb942a 100644 --- a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/projects/BuildScriptTemplateProcessor.kt +++ b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/projects/BuildScriptTemplateProcessor.kt @@ -4,56 +4,57 @@ import com.soywiz.korte.TeFunction import com.soywiz.korte.TemplateConfig import com.soywiz.korte.TemplateProvider import com.soywiz.korte.Templates -import kotlinx.coroutines.runBlocking import java.io.File +import kotlinx.coroutines.runBlocking /** - * Processor class for virtual build script files, used by Functional Tests. - * It utilizes a template engine to customize the processed output for the build scripts - * injected into the virtual projects, based around template files located within src/test/resources. + * Processor class for virtual build script files, used by Functional Tests. It utilizes a template + * engine to customize the processed output for the build scripts injected into the virtual + * projects, based around template files located within src/test/resources. */ class BuildScriptTemplateProcessor( - folder: File, - private val replacements: Map, - private val agpVersion: String, - private val gradleVersion: String, - private val junitVersion: String, + folder: File, + private val replacements: Map, + private val agpVersion: String, + private val gradleVersion: String, + private val junitVersion: String, ) { - private val renderer = Templates( - root = FileReadingTemplateProvider(folder), - cache = true, - config = TemplateConfig( - // Allow checking for AGP & Gradle versions inside the templates - extraFunctions = listOf( - TeFunction("atLeastAgp") { args -> - isVersionAtLeast(agpVersion, args[0].toDynamicString()) - }, - TeFunction("atLeastGradle") { args -> - isVersionAtLeast(gradleVersion, args[0].toDynamicString()) - }, - TeFunction("atLeastJUnit") { args -> - isVersionAtLeast(junitVersion, args[0].toDynamicString()) - } - ) - ) - ) + private val renderer = + Templates( + root = FileReadingTemplateProvider(folder), + cache = true, + config = + TemplateConfig( + // Allow checking for AGP & Gradle versions inside the templates + extraFunctions = + listOf( + TeFunction("atLeastAgp") { args -> + isVersionAtLeast(agpVersion, args[0].toDynamicString()) + }, + TeFunction("atLeastGradle") { args -> + isVersionAtLeast(gradleVersion, args[0].toDynamicString()) + }, + TeFunction("atLeastJUnit") { args -> + isVersionAtLeast(junitVersion, args[0].toDynamicString()) + }, + ) + ), + ) - fun process(fileName: String): String = runBlocking { - renderer.render(fileName, replacements) - } + fun process(fileName: String): String = runBlocking { renderer.render(fileName, replacements) } - /* Private */ + /* Private */ - private fun isVersionAtLeast(actual: String, required: String): Boolean { - val actualVersion = SemanticVersion(actual) - val requiredVersion = SemanticVersion(required) - return actualVersion >= requiredVersion - } + private fun isVersionAtLeast(actual: String, required: String): Boolean { + val actualVersion = SemanticVersion(actual) + val requiredVersion = SemanticVersion(required) + return actualVersion >= requiredVersion + } - private class FileReadingTemplateProvider(private val folder: File) : TemplateProvider { - override suspend fun get(template: String): String { - return File(folder, template).readText() + private class FileReadingTemplateProvider(private val folder: File) : TemplateProvider { + override suspend fun get(template: String): String { + return File(folder, template).readText() + } } - } } diff --git a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/projects/BuildScriptTemplateProcessorTests.kt b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/projects/BuildScriptTemplateProcessorTests.kt index 05341294..fa754895 100644 --- a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/projects/BuildScriptTemplateProcessorTests.kt +++ b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/projects/BuildScriptTemplateProcessorTests.kt @@ -1,141 +1,147 @@ package de.mannodermaus.gradle.plugins.junit5.util.projects import com.google.common.truth.Truth.assertThat +import java.io.File import kotlinx.coroutines.runBlocking import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.io.TempDir -import java.io.File class BuildScriptTemplateProcessorTests { - private lateinit var folder: File + private lateinit var folder: File - @BeforeEach - fun before(@TempDir folder: File) { - this.folder = folder - } + @BeforeEach + fun before(@TempDir folder: File) { + this.folder = folder + } - @Test - fun `works with brackets`() = runTest( - replacements = mapOf( - "VALUE" to "hello" - ), - rawText = """ + @Test + fun `works with brackets`() = + runTest( + replacements = mapOf("VALUE" to "hello"), + rawText = + """ val value = "{{ VALUE }}" if (123 == 456) { println("yolo") } """, - expectedText = """ + expectedText = + """ val value = "hello" if (123 == 456) { println("yolo") } - """ - ) + """, + ) - @Test - fun `substitution with existing value`() = runTest( - replacements = mapOf( - "VALUE" to "hello" - ), - rawText = """ + @Test + fun `substitution with existing value`() = + runTest( + replacements = mapOf("VALUE" to "hello"), + rawText = + """ val value = "{{ VALUE }}" """, - expectedText = """ + expectedText = + """ val value = "hello" - """ - ) + """, + ) - @Test - fun `substitution with missing value`() = runTest( - replacements = mapOf(), - rawText = """ + @Test + fun `substitution with missing value`() = + runTest( + replacements = mapOf(), + rawText = + """ val value = "{{ VALUE }}" """, - expectedText = """ + expectedText = + """ val value = "" - """ - ) + """, + ) - @Test - fun `ifelse with first block winning`() = runTest( - replacements = mapOf( - "VALUE" to true - ), - rawText = """ + @Test + fun `ifelse with first block winning`() = + runTest( + replacements = mapOf("VALUE" to true), + rawText = + """ {% if VALUE %} val value = 1234 {% else %} val value = 5678 {% endif %} """, - expectedText = """ + expectedText = + """ val value = 1234 - """ - ) + """, + ) - @Test - fun `ifelse with second block winning`() = runTest( - replacements = mapOf( - "VALUE" to false - ), - rawText = """ + @Test + fun `ifelse with second block winning`() = + runTest( + replacements = mapOf("VALUE" to false), + rawText = + """ {% if VALUE %} val value = 1234 {% else %} val value = 5678 {% endif %} """, - expectedText = """ + expectedText = + """ val value = 5678 - """ - ) + """, + ) - @Test - fun `ifelseif with first block winning`() = runTest( - replacements = mapOf( - "VALUE" to true, - "VALUE2" to true - ), - rawText = """ + @Test + fun `ifelseif with first block winning`() = + runTest( + replacements = mapOf("VALUE" to true, "VALUE2" to true), + rawText = + """ {% if VALUE %} val value = 1234 {% elseif VALUE2 %} val value = 5678 {% endif %} """, - expectedText = """ + expectedText = + """ val value = 1234 - """ - ) + """, + ) - @Test - fun `ifelseif with second block winning`() = runTest( - replacements = mapOf( - "VALUE" to false, - "VALUE2" to true - ), - rawText = """ + @Test + fun `ifelseif with second block winning`() = + runTest( + replacements = mapOf("VALUE" to false, "VALUE2" to true), + rawText = + """ {% if VALUE %} val value = 1234 {% elseif VALUE2 %} val value = 5678 {% endif %} """, - expectedText = """ + expectedText = + """ val value = 5678 - """ - ) + """, + ) - @Test - fun `ifelseif with third block winning`() = runTest( - replacements = mapOf( - "VALUE" to false, - "VALUE2" to false - ), - rawText = """ + @Test + fun `ifelseif with third block winning`() = + runTest( + replacements = mapOf("VALUE" to false, "VALUE2" to false), + rawText = + """ {% if VALUE %} val value = 1234 {% elseif VALUE2 %} @@ -144,43 +150,46 @@ class BuildScriptTemplateProcessorTests { val value = 9999 {% endif %} """, - expectedText = """ + expectedText = + """ val value = 9999 - """ - ) + """, + ) - @Test - fun `empty loop won't emit anything`() = runTest( - replacements = mapOf( - "VALUES" to listOf() - ), - rawText = """ + @Test + fun `empty loop won't emit anything`() = + runTest( + replacements = mapOf("VALUES" to listOf()), + rawText = + """ println("before") {% for item in VALUES %} println(item) {% end %} println("after") """, - expectedText = """ + expectedText = + """ println("before") println("after") - """ - ) + """, + ) - @Test - fun `loop will emit something`() = runTest( - replacements = mapOf( - "VALUES" to listOf("value 1", "value 2") - ), - rawText = """ + @Test + fun `loop will emit something`() = + runTest( + replacements = mapOf("VALUES" to listOf("value 1", "value 2")), + rawText = + """ println("before") {% for item in VALUES %} println("{{ item }}") {% end %} println("after") """, - expectedText = """ + expectedText = + """ println("before") println("value 1") @@ -188,23 +197,23 @@ class BuildScriptTemplateProcessorTests { println("value 2") println("after") - """ - ) + """, + ) - @Test - fun `loop with nested conditional`() = runTest( - replacements = mapOf( - "ENABLED" to false, - "VALUES" to listOf("value 1", "value 2") - ), - rawText = """ + @Test + fun `loop with nested conditional`() = + runTest( + replacements = mapOf("ENABLED" to false, "VALUES" to listOf("value 1", "value 2")), + rawText = + """ println("before") {% for item in VALUES %} {% if ENABLED %}println("{{ item }}"){% else %}print("{{ item }}"){% endif %} {% end %} println("after") """, - expectedText = """ + expectedText = + """ println("before") print("value 1") @@ -212,13 +221,15 @@ class BuildScriptTemplateProcessorTests { print("value 2") println("after") - """ - ) + """, + ) - @Test - fun `custom functions work`() = runTest( - replacements = emptyMap(), - rawText = """ + @Test + fun `custom functions work`() = + runTest( + replacements = emptyMap(), + rawText = + """ {% if atLeastAgp("4.2") %}println("AGP 1"){% endif %} {% if atLeastAgp("1000.0") %}println("AGP 2"){% endif %} {% if atLeastGradle("5.0") %}println("GRD 1"){% endif %} @@ -226,33 +237,36 @@ class BuildScriptTemplateProcessorTests { {% if atLeastJUnit("5.10.0") %}println("JU 1"){% endif %} {% if atLeastJUnit("1000.4.0") %}println("JU 2"){% endif %} """, - expectedText = """ + expectedText = + """ println("AGP 1") println("GRD 1") println("JU 1") - """ - ) + """, + ) - /* Private */ + /* Private */ - private fun runTest(replacements: Map, rawText: String, expectedText: String) = runBlocking { - // Dump the content into a temporary file with a known name, - // then run the processor over it and check its result - val file = File(folder, "file.kt").also { it.writeText(rawText) } - val processor = BuildScriptTemplateProcessor( - folder = folder, - replacements = replacements, - agpVersion = "7.0", - gradleVersion = "6.7", - junitVersion = "5.14.1", - ) + private fun runTest(replacements: Map, rawText: String, expectedText: String) = + runBlocking { + // Dump the content into a temporary file with a known name, + // then run the processor over it and check its result + val file = File(folder, "file.kt").also { it.writeText(rawText) } + val processor = + BuildScriptTemplateProcessor( + folder = folder, + replacements = replacements, + agpVersion = "7.0", + gradleVersion = "6.7", + junitVersion = "5.14.1", + ) - // Replace pound-signs with dollar signs (the tests use the former - // to avoid annoyances with Kotlin's string interpolation syntax) - assertThat(processor.process(file.name).trimIndent().trim()) - .isEqualTo(expectedText.trimIndent().trim()) - } -} \ No newline at end of file + // Replace pound-signs with dollar signs (the tests use the former + // to avoid annoyances with Kotlin's string interpolation syntax) + assertThat(processor.process(file.name).trimIndent().trim()) + .isEqualTo(expectedText.trimIndent().trim()) + } +} diff --git a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/projects/FunctionalTestProjectCreator.kt b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/projects/FunctionalTestProjectCreator.kt index 99b9bf26..48a460d4 100644 --- a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/projects/FunctionalTestProjectCreator.kt +++ b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/projects/FunctionalTestProjectCreator.kt @@ -6,10 +6,9 @@ import com.uchuhimo.konf.source.toml import de.mannodermaus.gradle.plugins.junit5.util.TestEnvironment import de.mannodermaus.gradle.plugins.junit5.util.TestedAgp import de.mannodermaus.gradle.plugins.junit5.util.TestedJUnit -import org.gradle.util.GradleVersion +import java.io.File import org.junit.jupiter.api.Assumptions.assumeTrue import org.opentest4j.TestAbortedException -import java.io.File private const val TEST_PROJECTS_RESOURCE = "/test-projects" private const val BUILD_GRADLE_TEMPLATE_NAME = "build.gradle.kts.template" @@ -21,7 +20,7 @@ private const val SRC_FOLDER_NAME = "src" class FunctionalTestProjectCreator( private val outputFolder: File, - private val environment: TestEnvironment + private val environment: TestEnvironment, ) { private val projectRootFolder: File @@ -33,15 +32,18 @@ class FunctionalTestProjectCreator( projectRootFolder = File(rootUrl.toURI()) // Collect all eligible test folders - allSpecs = projectRootFolder.listFiles() - ?.filter { it.isDirectory } - ?.mapNotNull { folder -> Spec.tryCreate(folder) } - ?: emptyList() + allSpecs = + projectRootFolder + .listFiles() + ?.filter { it.isDirectory } + ?.mapNotNull { folder -> Spec.tryCreate(folder) } ?: emptyList() } fun specNamed(name: String): Spec = allSpecs.firstOrNull { it.name == name } - ?: throw IllegalAccessException("No test project named '$name' found in src/test/resources/test-projects") + ?: throw IllegalAccessException( + "No test project named '$name' found in src/test/resources/test-projects" + ) @Throws(TestAbortedException::class) fun createProject(spec: Spec, agp: TestedAgp, junit: TestedJUnit): File { @@ -68,7 +70,8 @@ class FunctionalTestProjectCreator( File(projectFolder, "gradle.properties").bufferedWriter().use { file -> file.appendLine("android.useAndroidX = true") - // From AGP 9, test components are only generated for the debug build type; disable this behavior + // From AGP 9, test components are only generated for the debug build type; disable this + // behavior file.appendLine("android.onlyEnableUnitTestForTheTestedBuildType = false") } @@ -76,7 +79,8 @@ class FunctionalTestProjectCreator( val targetSrcFolder = File(projectFolder, "src").also { it.mkdir() } spec.srcFolder.copyRecursively(targetSrcFolder) - // Construct the build script from its raw template, using the environment properties as placeholders + // Construct the build script from its raw template, using the environment properties as + // placeholders val replacements = environment.envProps.mapKeys { it.key.toString() }.toMutableMap() replacements["AGP_VERSION"] = agp.version replacements["USE_KOTLIN"] = spec.useKotlin @@ -88,17 +92,16 @@ class FunctionalTestProjectCreator( replacements["DISABLE_TESTS_FOR_BUILD_TYPES"] = spec.disableTestsForBuildTypes replacements["JUNIT_VERSION"] = junit.fullVersion - agp.requiresCompileSdk?.let { - replacements["OVERRIDE_SDK_VERSION"] = it - } + agp.requiresCompileSdk?.let { replacements["OVERRIDE_SDK_VERSION"] = it } - val processor = BuildScriptTemplateProcessor( - folder = projectRootFolder, - replacements = replacements, - agpVersion = agp.version, - gradleVersion = agp.requiresGradle, - junitVersion = junit.fullVersion, - ) + val processor = + BuildScriptTemplateProcessor( + folder = projectRootFolder, + replacements = replacements, + agpVersion = agp.version, + gradleVersion = agp.requiresGradle, + junitVersion = junit.fullVersion, + ) processor.process(BUILD_GRADLE_TEMPLATE_NAME).also { result -> File(projectFolder, OUTPUT_BUILD_GRADLE_NAME).writeText(result) @@ -117,18 +120,14 @@ class FunctionalTestProjectCreator( // disable the test for plugin versions below that minimum requirement assumeTrue( SemanticVersion(agp.version) >= SemanticVersion(spec.minAgpVersion), - "This project requires AGP ${spec.minAgpVersion} and was disabled on this version." + "This project requires AGP ${spec.minAgpVersion} and was disabled on this version.", ) } } /* Types */ - class Spec private constructor( - val name: String, - val srcFolder: File, - config: Config - ) { + class Spec private constructor(val name: String, val srcFolder: File, config: Config) { val task = config[TomlSpec.Settings.task] val minAgpVersion = config[TomlSpec.Settings.minAgpVersion] val useKotlin = config[TomlSpec.Settings.useKotlin] @@ -140,9 +139,9 @@ class FunctionalTestProjectCreator( val allowSkipped = config[TomlSpec.Settings.allowSkipped] val expectedTests = config[TomlSpec.expectations] - val disableTestsForBuildTypes = config[TomlSpec.Settings.disableTestsForBuildTypes] - ?.split(",")?.map(String::trim) - ?: emptyList() + val disableTestsForBuildTypes = + config[TomlSpec.Settings.disableTestsForBuildTypes]?.split(",")?.map(String::trim) + ?: emptyList() companion object { fun tryCreate(folder: File): Spec? { @@ -184,13 +183,11 @@ class FunctionalTestProjectCreator( } } - /** - * Data holder for one set of expected tests within the virtual project - */ + /** Data holder for one set of expected tests within the virtual project */ data class ExpectedTests( val buildType: String, val productFlavor: String?, - private val tests: String + private val tests: String, ) { val testsList = tests.split(",").map(String::trim) } diff --git a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/projects/PluginSpecProjectCreator.kt b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/projects/PluginSpecProjectCreator.kt index 6bfcb0f6..a7646142 100644 --- a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/projects/PluginSpecProjectCreator.kt +++ b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/projects/PluginSpecProjectCreator.kt @@ -15,13 +15,13 @@ enum class Type(val pluginId: String? = null) { Unset, Application("com.android.application"), Library("com.android.library"), - DynamicFeature("com.android.dynamic-feature") + DynamicFeature("com.android.dynamic-feature"), } /** - * Creator class for test projects used specifically in the plugin-verification checks - * described by PluginSpec. This is different to the FunctionalTestProjectCreator, - * which utilizes actual source code located as a test resource. + * Creator class for test projects used specifically in the plugin-verification checks described by + * PluginSpec. This is different to the FunctionalTestProjectCreator, which utilizes actual source + * code located as a test resource. */ class PluginSpecProjectCreator(private val environment: TestEnvironment) { @@ -47,9 +47,8 @@ class PluginSpecProjectCreator(private val environment: TestEnvironment) { private var applyJacocoPlugin = false private var applyKotlinPlugin = false - private val project = ProjectBuilder.builder() - .withParent(parent) - .run { + private val project = + ProjectBuilder.builder().withParent(parent).run { if (name != null) { this.withName(name) } @@ -63,22 +62,21 @@ class PluginSpecProjectCreator(private val environment: TestEnvironment) { fun asAndroidLibrary() = setProjectTypeIfUnsetTo(Type.Library) - fun applyJUnit5Plugin(state: Boolean = true, configuration: ((AndroidJUnitPlatformExtension) -> Unit)? = null) = - apply { - this.applyJUnit5Plugin = if (state) { + fun applyJUnit5Plugin( + state: Boolean = true, + configuration: ((AndroidJUnitPlatformExtension) -> Unit)? = null, + ) = apply { + this.applyJUnit5Plugin = + if (state) { configuration ?: {} } else { null } - } - - fun applyJacocoPlugin(state: Boolean = true) = apply { - this.applyJacocoPlugin = state } - fun applyKotlinPlugin(state: Boolean = true) = apply { - this.applyKotlinPlugin = state - } + fun applyJacocoPlugin(state: Boolean = true) = apply { this.applyJacocoPlugin = state } + + fun applyKotlinPlugin(state: Boolean = true) = apply { this.applyKotlinPlugin = state } fun build(): Project { // Write out required Android file structure diff --git a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/projects/SemanticVersion.kt b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/projects/SemanticVersion.kt index 29f5e561..f9f35f1e 100644 --- a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/projects/SemanticVersion.kt +++ b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/projects/SemanticVersion.kt @@ -5,63 +5,63 @@ import java.util.Locale private val NUMERICAL_REGEX = Regex("(\\d+)") /** - * Wrapper for a semantic version string (e.g. "6.0" or "7.1.5-alpha-12"), - * interpreting that string as a numerical value eligible for comparisons against other objects. + * Wrapper for a semantic version string (e.g. "6.0" or "7.1.5-alpha-12"), interpreting that string + * as a numerical value eligible for comparisons against other objects. */ -class SemanticVersion(version: String): Comparable { - val stableValue: Int = version.extractStableValue() - val suffixValue: Int = version.extractSuffixValue() +class SemanticVersion(version: String) : Comparable { + val stableValue: Int = version.extractStableValue() + val suffixValue: Int = version.extractSuffixValue() - override fun compareTo(other: SemanticVersion): Int { - val result = this.stableValue.compareTo(other.stableValue) - return if (result == 0) { - this.suffixValue.compareTo(other.suffixValue) - } else { - result + override fun compareTo(other: SemanticVersion): Int { + val result = this.stableValue.compareTo(other.stableValue) + return if (result == 0) { + this.suffixValue.compareTo(other.suffixValue) + } else { + result + } } - } } /* Private */ private fun String.extractStableValue(): Int { - val stripped = this.substringBefore('-') - val split = stripped.split('.').map { - it.toIntOrNull() ?: throw IllegalArgumentException("unknown stable value for version: $this") - } + val stripped = this.substringBefore('-') + val split = + stripped.split('.').map { + it.toIntOrNull() + ?: throw IllegalArgumentException("unknown stable value for version: $this") + } - require(split.size in 2..3) { - "unsupported number of components for version: $this" - } + require(split.size in 2..3) { "unsupported number of components for version: $this" } - // Add up the components, with the patch component being optional - return split[0] * 10000 + - split[1] * 100 + - if (split.size > 2) split[2] else 0 + // Add up the components, with the patch component being optional + return split[0] * 10000 + split[1] * 100 + if (split.size > 2) split[2] else 0 } private fun String.extractSuffixValue(): Int { - if ('-' in this) { - val suffix = this - .substringAfter('-') - .lowercase(Locale.ROOT) - - // Find known suffix types - val suffixMultiplier = when { - "alpha" in suffix -> 1 - "beta" in suffix -> 100 - "rc" in suffix -> 10000 - else -> throw IllegalArgumentException("unknown suffix category for version: $this") - } + if ('-' in this) { + val suffix = this.substringAfter('-').lowercase(Locale.ROOT) - // Find numerical value of suffix - val numericalSuffix = NUMERICAL_REGEX.find(suffix)?.groupValues?.lastOrNull()?.toIntOrNull() - ?: throw IllegalArgumentException("unknown numerical suffix value for version: $this") + // Find known suffix types + val suffixMultiplier = + when { + "alpha" in suffix -> 1 + "beta" in suffix -> 100 + "rc" in suffix -> 10000 + else -> throw IllegalArgumentException("unknown suffix category for version: $this") + } - return suffixMultiplier * numericalSuffix + // Find numerical value of suffix + val numericalSuffix = + NUMERICAL_REGEX.find(suffix)?.groupValues?.lastOrNull()?.toIntOrNull() + ?: throw IllegalArgumentException( + "unknown numerical suffix value for version: $this" + ) - } else { - // Not a preview version - use highest possible suffix value to be "newer" than any preview could be - return Int.MAX_VALUE - } + return suffixMultiplier * numericalSuffix + } else { + // Not a preview version - use highest possible suffix value to be "newer" than any preview + // could be + return Int.MAX_VALUE + } } diff --git a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/projects/SemanticVersionTests.kt b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/projects/SemanticVersionTests.kt index 08a8a35f..89ca60fe 100644 --- a/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/projects/SemanticVersionTests.kt +++ b/plugin/android-junit5/src/test/kotlin/de/mannodermaus/gradle/plugins/junit5/util/projects/SemanticVersionTests.kt @@ -9,75 +9,84 @@ import org.junit.jupiter.params.provider.MethodSource @Suppress("unused") class SemanticVersionTests { - companion object { - @JvmStatic - fun dataParseTests() = listOf( - Arguments.of("5.0", 50000, Int.MAX_VALUE), - Arguments.of("6.6.1-alpha1", 60601, 1), - Arguments.of("6.6.1-beta1", 60601, 100), - Arguments.of("6.6.1", 60601, Int.MAX_VALUE), - Arguments.of("6.6-ALPHA-1", 60600, 1), - Arguments.of("6.6-alpha1", 60600, 1), - Arguments.of("6.6-alpha12", 60600, 12), - Arguments.of("7.0.1-beta05", 70001, 500), - Arguments.of("7.0.1-beta5", 70001, 500), - Arguments.of("8.0.0-rc1", 80000, 10000), - Arguments.of("8.0.0-rc-1", 80000, 10000), - Arguments.of("11.4.3-rc-10", 110403, 100000), - Arguments.of("99.15.67", 991567, Int.MAX_VALUE) - ) + companion object { + @JvmStatic + fun dataParseTests() = + listOf( + Arguments.of("5.0", 50000, Int.MAX_VALUE), + Arguments.of("6.6.1-alpha1", 60601, 1), + Arguments.of("6.6.1-beta1", 60601, 100), + Arguments.of("6.6.1", 60601, Int.MAX_VALUE), + Arguments.of("6.6-ALPHA-1", 60600, 1), + Arguments.of("6.6-alpha1", 60600, 1), + Arguments.of("6.6-alpha12", 60600, 12), + Arguments.of("7.0.1-beta05", 70001, 500), + Arguments.of("7.0.1-beta5", 70001, 500), + Arguments.of("8.0.0-rc1", 80000, 10000), + Arguments.of("8.0.0-rc-1", 80000, 10000), + Arguments.of("11.4.3-rc-10", 110403, 100000), + Arguments.of("99.15.67", 991567, Int.MAX_VALUE), + ) - @JvmStatic - fun dataCompareTo() = listOf( - Arguments.of("4.4.0", "5.7.2", -1), - Arguments.of("4.4.0", "4.5.0", -1), - Arguments.of("4.4.2", "4.4.1", 1), - Arguments.of("4.4.0-alpha-1", "4.4.0-alpha5", -1), - Arguments.of("7.2-alpha-4", "7.2-beta-6", -1), - Arguments.of("7.4-beta-1", "7.3-beta-2", 1), - Arguments.of("8.0", "8.0.0-alpha-2", 1), - Arguments.of("2.5", "2.5.0", 0), - Arguments.of("4.2-rc-50", "4.2", -1), - Arguments.of("4.2.1-rc-50", "4.2", 1) - ) + @JvmStatic + fun dataCompareTo() = + listOf( + Arguments.of("4.4.0", "5.7.2", -1), + Arguments.of("4.4.0", "4.5.0", -1), + Arguments.of("4.4.2", "4.4.1", 1), + Arguments.of("4.4.0-alpha-1", "4.4.0-alpha5", -1), + Arguments.of("7.2-alpha-4", "7.2-beta-6", -1), + Arguments.of("7.4-beta-1", "7.3-beta-2", 1), + Arguments.of("8.0", "8.0.0-alpha-2", 1), + Arguments.of("2.5", "2.5.0", 0), + Arguments.of("4.2-rc-50", "4.2", -1), + Arguments.of("4.2.1-rc-50", "4.2", 1), + ) - @JvmStatic - fun dataIncorrectInput() = listOf( - Arguments.of("2", "unsupported number of components for version: %s"), - Arguments.of("1.6.2.0", "unsupported number of components for version: %s"), - Arguments.of("ONE DOT TWO", "unknown stable value for version: %s"), - Arguments.of("ONE DOT TWO-alpha-1", "unknown stable value for version: %s"), - Arguments.of("2.zErO.6", "unknown stable value for version: %s"), - Arguments.of("1.5-preview-4", "unknown suffix category for version: %s"), - Arguments.of("3.2.0-3", "unknown suffix category for version: %s"), - Arguments.of("2.4-ALPHA", "unknown numerical suffix value for version: %s") - ) - } - - @MethodSource("dataParseTests") - @ParameterizedTest - fun `parse version into stable and suffix parts correctly`(input: String, expectedStable: Int, expectedSuffix: Int) { - val actual = SemanticVersion(input) + @JvmStatic + fun dataIncorrectInput() = + listOf( + Arguments.of("2", "unsupported number of components for version: %s"), + Arguments.of("1.6.2.0", "unsupported number of components for version: %s"), + Arguments.of("ONE DOT TWO", "unknown stable value for version: %s"), + Arguments.of("ONE DOT TWO-alpha-1", "unknown stable value for version: %s"), + Arguments.of("2.zErO.6", "unknown stable value for version: %s"), + Arguments.of("1.5-preview-4", "unknown suffix category for version: %s"), + Arguments.of("3.2.0-3", "unknown suffix category for version: %s"), + Arguments.of("2.4-ALPHA", "unknown numerical suffix value for version: %s"), + ) + } - assertThat(actual.stableValue).isEqualTo(expectedStable) - assertThat(actual.suffixValue).isEqualTo(expectedSuffix) - } + @MethodSource("dataParseTests") + @ParameterizedTest + fun `parse version into stable and suffix parts correctly`( + input: String, + expectedStable: Int, + expectedSuffix: Int, + ) { + val actual = SemanticVersion(input) - @MethodSource("dataCompareTo") - @ParameterizedTest - fun `compares versions correctly`(version1: SemanticVersion, version2: SemanticVersion, expectedResult: Int) { - val actualResult = version1.compareTo(version2) + assertThat(actual.stableValue).isEqualTo(expectedStable) + assertThat(actual.suffixValue).isEqualTo(expectedSuffix) + } - assertThat(actualResult).isEqualTo(expectedResult) - } + @MethodSource("dataCompareTo") + @ParameterizedTest + fun `compares versions correctly`( + version1: SemanticVersion, + version2: SemanticVersion, + expectedResult: Int, + ) { + val actualResult = version1.compareTo(version2) - @MethodSource("dataIncorrectInput") - @ParameterizedTest - fun `throws on incorrect inputs`(version: String, expectedError: String) { - val exception = assertThrows { - SemanticVersion(version) + assertThat(actualResult).isEqualTo(expectedResult) } - assertThat(exception).hasMessageThat().isEqualTo(expectedError.format(version)) - } + @MethodSource("dataIncorrectInput") + @ParameterizedTest + fun `throws on incorrect inputs`(version: String, expectedError: String) { + val exception = assertThrows { SemanticVersion(version) } + + assertThat(exception).hasMessageThat().isEqualTo(expectedError.format(version)) + } } diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index 78d51609..c93e38a6 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -1,3 +1,4 @@ +import com.ncorti.ktfmt.gradle.KtfmtExtension import org.gradle.api.tasks.testing.logging.TestExceptionFormat import org.gradle.api.tasks.testing.logging.TestLogEvent import org.jetbrains.kotlin.gradle.dsl.JvmTarget @@ -9,6 +10,7 @@ plugins { alias(libs.plugins.dokka).apply(false) alias(libs.plugins.kotlin.android).apply(false) alias(libs.plugins.kotlin.jvm).apply(false) + alias(libs.plugins.ktfmt).apply(false) alias(libs.plugins.shadow).apply(false) alias(libs.plugins.kotlin.binarycompvalidator) @@ -19,6 +21,12 @@ subprojects { val jvmTarget = JvmTarget.JVM_17 val javaVersion = JavaVersion.toVersion(jvmTarget.target) + // Configure code formatting + apply(plugin = "com.ncorti.ktfmt.gradle") + configure { + kotlinLangStyle() + } + // Configure Kotlin plugins.withType { tasks.withType>().configureEach { From 6fff1bbaabf9669cfbbc764f1f4166ee046b2553 Mon Sep 17 00:00:00 2001 From: Marcel Schnelle Date: Tue, 30 Dec 2025 15:26:29 +0100 Subject: [PATCH 2/3] Introduce ktfmt --- instrumentation/build.gradle.kts | 8 + instrumentation/compose/build.gradle.kts | 4 +- .../compose/ClassComposeExtensionTests.kt | 41 ++-- .../ExistingActivityComposeExtensionTests.kt | 15 +- .../compose/FieldComposeExtensionTests.kt | 31 +-- .../junit5/compose/ExistingActivity.kt | 4 +- .../junit5/compose/AndroidComposeExtension.kt | 111 +++++---- .../junit5/compose/ComposeContext.kt | 36 ++- .../junit5/compose/ComposeExtension.kt | 28 +-- .../junit5/compose/ComposeContextTests.kt | 2 +- instrumentation/core/build.gradle.kts | 2 - .../mannodermaus/junit5/BaselineJUnit4Test.kt | 13 +- .../junit5/KotlinInstrumentationTests.kt | 61 ++--- .../de/mannodermaus/junit5/TaggedTests.kt | 4 +- .../inheritance/KotlinInheritanceTests.kt | 1 + .../junit5/otherpackage/AnotherTest.kt | 4 +- .../de/mannodermaus/junit5/TestActivity.kt | 16 +- .../de/mannodermaus/junit5/CoreConstants.kt | 4 +- .../internal/compat/ExtensionContextCompat.kt | 6 +- .../junit5/ActivityScenarioExtension.kt | 90 +++---- .../junit5/DeprecatedCoreConstants.kt | 2 +- .../condition/DisabledIfBuildConfigValue.kt | 5 +- .../condition/DisabledOnManufacturer.kt | 2 +- .../junit5/condition/DisabledOnSdkVersion.kt | 2 +- .../condition/EnabledIfBuildConfigValue.kt | 5 +- .../junit5/condition/EnabledOnManufacturer.kt | 2 +- .../junit5/condition/EnabledOnSdkVersion.kt | 2 +- .../DisabledIfBuildConfigValueCondition.kt | 26 +- .../DisabledOnManufacturerCondition.kt | 4 +- .../internal/DisabledOnSdkVersionCondition.kt | 7 +- .../EnabledIfBuildConfigValueCondition.kt | 26 +- .../EnabledOnManufacturerCondition.kt | 12 +- .../internal/EnabledOnSdkVersionCondition.kt | 7 +- .../internal/utils/BuildConfigValueUtils.kt | 16 +- .../de/mannodermaus/junit5/CoreConstants.kt | 4 +- .../internal/compat/ExtensionContextCompat.kt | 6 +- .../AbstractExecutionConditionTests.kt | 59 +++-- ...isabledIfBuildConfigValueConditionTests.kt | 107 ++++----- ...abledIfBuildConfigValueIntegrationTests.kt | 51 ++-- .../DisabledOnManufacturerConditionTests.kt | 119 +++++---- .../DisabledOnManufacturerIntegrationTests.kt | 70 +++--- .../DisabledOnSdkVersionConditionTests.kt | 135 +++++------ .../DisabledOnSdkVersionIntegrationTests.kt | 81 +++---- ...EnabledIfBuildConfigValueConditionTests.kt | 107 ++++----- ...abledIfBuildConfigValueIntegrationTests.kt | 51 ++-- .../EnabledOnManufacturerConditionTests.kt | 119 +++++---- .../EnabledOnManufacturerIntegrationTests.kt | 70 +++--- .../EnabledOnSdkVersionConditionTests.kt | 134 +++++------ .../EnabledOnSdkVersionIntegrationTests.kt | 81 +++---- .../mannodermaus/junit5/util/ResourceLocks.kt | 4 +- instrumentation/extensions/build.gradle.kts | 4 +- .../extensions/GrantPermissionExtension.kt | 20 +- .../GrantPermissionExtensionTests.kt | 15 +- instrumentation/runner/build.gradle.kts | 4 +- .../junit5/internal/RunnerConstants.kt | 4 +- .../discovery/EmptyConfigurationParameters.kt | 11 +- .../internal/runners/TestPlanAdapter.kt | 17 +- .../junit5/AndroidJUnitFrameworkBuilder.kt | 59 ++--- .../junit5/internal/LibcoreAccess.kt | 22 +- .../internal/discovery/EmptyTestPlan.kt | 32 +-- .../internal/discovery/GeneratedFilters.kt | 33 +-- .../internal/discovery/ParsedSelectors.kt | 49 ++-- .../internal/discovery/PropertiesParser.kt | 3 +- .../internal/discovery/ShardingFilter.kt | 12 +- .../internal/dummy/JupiterTestMethodFinder.kt | 12 +- .../internal/extensions/TestIdentifierExt.kt | 25 +- .../internal/formatters/TestNameFormatter.kt | 14 +- .../internal/runners/AndroidJUnitFramework.kt | 39 +-- .../AndroidJUnitPlatformRunnerListener.kt | 23 +- .../runners/AndroidJUnitPlatformTestTree.kt | 121 +++++----- .../internal/runners/DummyJUnitFramework.kt | 12 +- .../runners/JUnitFrameworkRunnerFactory.kt | 22 +- .../runners/JUnitFrameworkRunnerParams.kt | 31 ++- .../notification/FilteredRunListener.kt | 6 +- .../notification/ParallelRunNotifier.kt | 157 ++++++------ .../junit5/internal/RunnerConstants.kt | 4 +- .../discovery/EmptyConfigurationParameters.kt | 8 +- .../internal/runners/TestPlanAdapter.kt | 17 +- .../AndroidJUnitFrameworkBuilderTests.kt | 76 +++--- .../de/mannodermaus/junit5/TestClasses.kt | 125 ++++------ .../de/mannodermaus/junit5/TestHelpers.kt | 27 ++- .../discovery/PropertiesParserTests.kt | 56 ++--- .../dummy/JupiterTestMethodFinderTests.kt | 51 ++-- .../formatters/TestNameFormatterTests.kt | 101 ++++---- .../runners/AndroidJUnitFrameworkTests.kt | 79 +++--- .../AndroidJUnitPlatformTestTreeTests.kt | 64 +++-- instrumentation/sample/build.gradle.kts | 15 +- .../de/mannodermaus/sample/ActivityOneTest.kt | 107 ++++----- .../sample/TestRunningOnJUnit4.kt | 40 +-- .../sample/TestTemplateExampleTests.kt | 29 +-- .../mannodermaus/junit5/sample/ActivityOne.kt | 32 +-- .../mannodermaus/sample/ExampleKotlinTest.kt | 227 +++++++++--------- .../testutil-reflect/build.gradle.kts | 4 +- .../junit5/testutil/reflect/Reflections.kt | 2 +- .../junit5/testutil/AndroidBuildUtils.kt | 24 +- .../junit5/testutil/CollectingRunListener.kt | 6 +- .../junit5/testutil/StubInstrumentation.kt | 9 +- 97 files changed, 1736 insertions(+), 1911 deletions(-) diff --git a/instrumentation/build.gradle.kts b/instrumentation/build.gradle.kts index c6732449..efed7b22 100644 --- a/instrumentation/build.gradle.kts +++ b/instrumentation/build.gradle.kts @@ -1,5 +1,6 @@ import com.android.build.api.dsl.LibraryExtension import com.android.build.gradle.BaseExtension +import com.ncorti.ktfmt.gradle.KtfmtExtension import extensions.capitalized import org.gradle.api.tasks.testing.logging.TestExceptionFormat import org.gradle.api.tasks.testing.logging.TestLogEvent @@ -17,6 +18,7 @@ plugins { alias(libs.plugins.dokka).apply(false) alias(libs.plugins.kotlin.android).apply(false) alias(libs.plugins.kotlin.jvm).apply(false) + alias(libs.plugins.ktfmt).apply(false) alias(libs.plugins.kotlin.binarycompvalidator) alias(libs.plugins.publish) @@ -36,6 +38,12 @@ subprojects { val jvmTarget = JvmTarget.JVM_17 val javaVersion = JavaVersion.toVersion(jvmTarget.target) + // Configure code formatting + apply(plugin = "com.ncorti.ktfmt.gradle") + configure { + kotlinLangStyle() + } + // Configure Kotlin plugins.withType { tasks.withType>().configureEach { diff --git a/instrumentation/compose/build.gradle.kts b/instrumentation/compose/build.gradle.kts index 650b0ca4..04a32d7a 100644 --- a/instrumentation/compose/build.gradle.kts +++ b/instrumentation/compose/build.gradle.kts @@ -15,9 +15,7 @@ android { "de.mannodermaus.junit5.AndroidJUnitFrameworkBuilder" } - buildFeatures { - compose = true - } + buildFeatures { compose = true } } junitPlatform { diff --git a/instrumentation/compose/src/androidTest/java/de/mannodermaus/junit5/compose/ClassComposeExtensionTests.kt b/instrumentation/compose/src/androidTest/java/de/mannodermaus/junit5/compose/ClassComposeExtensionTests.kt index 278911d4..39f849b5 100644 --- a/instrumentation/compose/src/androidTest/java/de/mannodermaus/junit5/compose/ClassComposeExtensionTests.kt +++ b/instrumentation/compose/src/androidTest/java/de/mannodermaus/junit5/compose/ClassComposeExtensionTests.kt @@ -19,13 +19,7 @@ import org.junit.jupiter.params.provider.ValueSource @ExtendWith(AndroidComposeExtension::class) class ClassComposeExtensionTests { - @ValueSource( - strings = [ - "click me", - "touch me", - "jfc it actually works" - ] - ) + @ValueSource(strings = ["click me", "touch me", "jfc it actually works"]) @ParameterizedTest fun test(buttonLabel: String, extension: AndroidComposeExtension) = extension.use { @@ -34,9 +28,7 @@ class ClassComposeExtensionTests { var counter by remember { mutableStateOf(0) } Text(text = "Clicked: $counter") - Button(onClick = { counter++ }) { - Text(text = buttonLabel) - } + Button(onClick = { counter++ }) { Text(text = buttonLabel) } } } @@ -46,24 +38,23 @@ class ClassComposeExtensionTests { } @Test - fun anotherTest(extension: ComposeExtension) = extension.use { - setContent { - Column { - var showDetails by remember { mutableStateOf(false) } + fun anotherTest(extension: ComposeExtension) = + extension.use { + setContent { + Column { + var showDetails by remember { mutableStateOf(false) } - Text("Hello world") - if (showDetails) { - Text("Extra details") - } + Text("Hello world") + if (showDetails) { + Text("Extra details") + } - Button(onClick = { showDetails = !showDetails }) { - Text("click") + Button(onClick = { showDetails = !showDetails }) { Text("click") } } } - } - onNodeWithText("Extra details").assertDoesNotExist() - onNodeWithText("click").performClick() - onNodeWithText("Extra details").assertIsDisplayed() - } + onNodeWithText("Extra details").assertDoesNotExist() + onNodeWithText("click").performClick() + onNodeWithText("Extra details").assertIsDisplayed() + } } diff --git a/instrumentation/compose/src/androidTest/java/de/mannodermaus/junit5/compose/ExistingActivityComposeExtensionTests.kt b/instrumentation/compose/src/androidTest/java/de/mannodermaus/junit5/compose/ExistingActivityComposeExtensionTests.kt index 2d6dfa67..f0cf41f7 100644 --- a/instrumentation/compose/src/androidTest/java/de/mannodermaus/junit5/compose/ExistingActivityComposeExtensionTests.kt +++ b/instrumentation/compose/src/androidTest/java/de/mannodermaus/junit5/compose/ExistingActivityComposeExtensionTests.kt @@ -19,18 +19,9 @@ class ExistingActivityComposeExtensionTests { @OptIn(ExperimentalTestApi::class) val extension = createAndroidComposeExtension() - @BeforeAll - fun beforeAll() = extension.use { - onNodeWithText("click").performClick() - } + @BeforeAll fun beforeAll() = extension.use { onNodeWithText("click").performClick() } - @BeforeEach - fun beforeEach() = extension.use { - onNodeWithText("click").performClick() - } + @BeforeEach fun beforeEach() = extension.use { onNodeWithText("click").performClick() } - @Test - fun test() = extension.use { - onNodeWithText("Clicked: 2").assertIsDisplayed() - } + @Test fun test() = extension.use { onNodeWithText("Clicked: 2").assertIsDisplayed() } } diff --git a/instrumentation/compose/src/androidTest/java/de/mannodermaus/junit5/compose/FieldComposeExtensionTests.kt b/instrumentation/compose/src/androidTest/java/de/mannodermaus/junit5/compose/FieldComposeExtensionTests.kt index 070c0333..3a1574bf 100644 --- a/instrumentation/compose/src/androidTest/java/de/mannodermaus/junit5/compose/FieldComposeExtensionTests.kt +++ b/instrumentation/compose/src/androidTest/java/de/mannodermaus/junit5/compose/FieldComposeExtensionTests.kt @@ -22,28 +22,21 @@ class FieldComposeExtensionTests { @OptIn(ExperimentalTestApi::class) val extension = createComposeExtension() - @ValueSource( - strings = [ - "click me", - "touch me", - "jfc it actually works" - ] - ) + @ValueSource(strings = ["click me", "touch me", "jfc it actually works"]) @ParameterizedTest - fun test(buttonLabel: String) = extension.use { - setContent { - Column { - var counter by remember { mutableStateOf(0) } + fun test(buttonLabel: String) = + extension.use { + setContent { + Column { + var counter by remember { mutableStateOf(0) } - Text(text = "Clicked: $counter") - Button(onClick = { counter++ }) { - Text(text = buttonLabel) + Text(text = "Clicked: $counter") + Button(onClick = { counter++ }) { Text(text = buttonLabel) } } } - } - onNodeWithText("Clicked: 0").assertIsDisplayed() - onNodeWithText(buttonLabel).performClick() - onNodeWithText("Clicked: 1").assertIsDisplayed() - } + onNodeWithText("Clicked: 0").assertIsDisplayed() + onNodeWithText(buttonLabel).performClick() + onNodeWithText("Clicked: 1").assertIsDisplayed() + } } diff --git a/instrumentation/compose/src/debug/java/de/mannodermaus/junit5/compose/ExistingActivity.kt b/instrumentation/compose/src/debug/java/de/mannodermaus/junit5/compose/ExistingActivity.kt index 41823d61..f44348db 100644 --- a/instrumentation/compose/src/debug/java/de/mannodermaus/junit5/compose/ExistingActivity.kt +++ b/instrumentation/compose/src/debug/java/de/mannodermaus/junit5/compose/ExistingActivity.kt @@ -20,9 +20,7 @@ public class ExistingActivity : ComponentActivity() { var counter by remember { mutableIntStateOf(0) } Text(text = "Clicked: $counter") - Button(onClick = { counter++ }) { - Text("click") - } + Button(onClick = { counter++ }) { Text("click") } } } } diff --git a/instrumentation/compose/src/main/java/de/mannodermaus/junit5/compose/AndroidComposeExtension.kt b/instrumentation/compose/src/main/java/de/mannodermaus/junit5/compose/AndroidComposeExtension.kt index b21ba917..4bc27779 100644 --- a/instrumentation/compose/src/main/java/de/mannodermaus/junit5/compose/AndroidComposeExtension.kt +++ b/instrumentation/compose/src/main/java/de/mannodermaus/junit5/compose/AndroidComposeExtension.kt @@ -18,9 +18,9 @@ import org.junit.jupiter.api.extension.ParameterResolver import org.junit.jupiter.api.extension.RegisterExtension /** - * Factory method to provide a JUnit 5 extension for Compose using its [RegisterExtension] API - * for field injection. Prefer this over [createAndroidComposeExtension] if you don't care - * about the specific activity class to use under the hood. + * Factory method to provide a JUnit 5 extension for Compose using its [RegisterExtension] API for + * field injection. Prefer this over [createAndroidComposeExtension] if you don't care about the + * specific activity class to use under the hood. * * ``` * class MyTests { @@ -35,62 +35,72 @@ public fun createComposeExtension(): ComposeExtension = createAndroidComposeExtension() /** - * Factory method to provide a JUnit 5 extension for Compose using its [RegisterExtension] API - * for field injection. Prefer this over [createComposeExtension] if your tests require a custom Activity. - * This is usually the case for tests where the Compose content is set by that Activity, instead of - * via [ComposeContext.setContent], provided through the extension. Make sure that you add the provided - * Activity to your app's manifest file. + * Factory method to provide a JUnit 5 extension for Compose using its [RegisterExtension] API for + * field injection. Prefer this over [createComposeExtension] if your tests require a custom + * Activity. This is usually the case for tests where the Compose content is set by that Activity, + * instead of via [ComposeContext.setContent], provided through the extension. Make sure that you + * add the provided Activity to your app's manifest file. */ @ExperimentalTestApi -public inline fun createAndroidComposeExtension(): AndroidComposeExtension { +public inline fun createAndroidComposeExtension(): + AndroidComposeExtension { return createAndroidComposeExtension(A::class.java) } /** - * Factory method to provide a JUnit 5 extension for Compose using its [RegisterExtension] API - * for field injection. Prefer this over [createComposeExtension] if your tests require a custom Activity. - * This is usually the case for tests where the Compose content is set by that Activity, instead of - * via [ComposeContext.setContent], provided through the extension. Make sure that you add the provided - * Activity to your app's manifest file. This variant allows you to provide a custom [ActivityScenario] - * which is useful in cases where you may want to launch an activity with a custom intent, for example. + * Factory method to provide a JUnit 5 extension for Compose using its [RegisterExtension] API for + * field injection. Prefer this over [createComposeExtension] if your tests require a custom + * Activity. This is usually the case for tests where the Compose content is set by that Activity, + * instead of via [ComposeContext.setContent], provided through the extension. Make sure that you + * add the provided Activity to your app's manifest file. This variant allows you to provide a + * custom [ActivityScenario] which is useful in cases where you may want to launch an activity with + * a custom intent, for example. */ @ExperimentalTestApi -public inline fun createAndroidComposeExtension(noinline scenarioSupplier: () -> ActivityScenario): AndroidComposeExtension { +public inline fun createAndroidComposeExtension( + noinline scenarioSupplier: () -> ActivityScenario +): AndroidComposeExtension { return createAndroidComposeExtension(A::class.java, scenarioSupplier) } /** - * Factory method to provide a JUnit 5 extension for Compose using its [RegisterExtension] API - * for field injection. Prefer this over [createComposeExtension] if your tests require a custom Activity. - * This is usually the case for tests where the Compose content is set by that Activity, instead of - * via [ComposeContext.setContent], provided through the extension. Make sure that you add the provided - * Activity to your app's manifest file. You may also provide an optional [ActivityScenario] supplier - * which is useful in cases where you may want to launch an activity with a custom intent,for example. + * Factory method to provide a JUnit 5 extension for Compose using its [RegisterExtension] API for + * field injection. Prefer this over [createComposeExtension] if your tests require a custom + * Activity. This is usually the case for tests where the Compose content is set by that Activity, + * instead of via [ComposeContext.setContent], provided through the extension. Make sure that you + * add the provided Activity to your app's manifest file. You may also provide an optional + * [ActivityScenario] supplier which is useful in cases where you may want to launch an activity + * with a custom intent,for example. */ @ExperimentalTestApi -public fun createAndroidComposeExtension(activityClass: Class, - scenarioSupplier: () -> ActivityScenario = { ActivityScenario.launch(activityClass) }): AndroidComposeExtension { +public fun createAndroidComposeExtension( + activityClass: Class, + scenarioSupplier: () -> ActivityScenario = { ActivityScenario.launch(activityClass) }, +): AndroidComposeExtension { return AndroidComposeExtension(scenarioSupplier) } /** - * A JUnit 5 [Extension] that allows you to test and control [Composable]s and application using Compose. - * The functionality of testing Compose is provided by means of the [runComposeTest] method, - * which receives a [ComposeContext] from which the test can be orchestrated. The test will block - * until the app or composable is idle, to ensure the tests are deterministic. + * A JUnit 5 [Extension] that allows you to test and control [Composable]s and application using + * Compose. The functionality of testing Compose is provided by means of the [runComposeTest] + * method, which receives a [ComposeContext] from which the test can be orchestrated. The test will + * block until the app or composable is idle, to ensure the tests are deterministic. * - * This extension can be added to any JUnit 5 class using the [ExtendWith] annotation, - * or registered globally through a configuration file. Alternatively, you can instantiate the extension - * in a field within the test class using any of the [createComposeExtension] or + * This extension can be added to any JUnit 5 class using the [ExtendWith] annotation, or registered + * globally through a configuration file. Alternatively, you can instantiate the extension in a + * field within the test class using any of the [createComposeExtension] or * [createAndroidComposeExtension] factory methods. */ @SuppressLint("NewApi") @OptIn(ExperimentalTestApi::class) public class AndroidComposeExtension -internal constructor( - private val scenarioSupplier: () -> ActivityScenario -) : ComposeExtension, BeforeEachCallback, BeforeTestExecutionCallback, AfterTestExecutionCallback, - AfterEachCallback, ParameterResolver { +internal constructor(private val scenarioSupplier: () -> ActivityScenario) : + ComposeExtension, + BeforeEachCallback, + BeforeTestExecutionCallback, + AfterTestExecutionCallback, + AfterEachCallback, + ParameterResolver { // Management of pending test operations private val pendingPrepareBlocks = mutableListOf Unit>() @@ -99,21 +109,18 @@ internal constructor( // Instantiated by JUnit 5 @Suppress("UNCHECKED_CAST", "unused") - internal constructor() : this( - scenarioSupplier = { - ActivityScenario.launch(ComponentActivity::class.java) as ActivityScenario - } - ) + internal constructor() : + this( + scenarioSupplier = { + ActivityScenario.launch(ComponentActivity::class.java) as ActivityScenario + } + ) public val scenario: ActivityScenario - get() = checkNotNull(_scenario) { - "Activity scenario could not be launched" - } + get() = checkNotNull(_scenario) { "Activity scenario could not be launched" } public val activity: A - get() = checkNotNull(environment?.test?.activity) { - "Host activity not found" - } + get() = checkNotNull(environment?.test?.activity) { "Host activity not found" } private var state = STATE_INIT @@ -136,7 +143,9 @@ internal constructor( } STATE_CALLED -> { - throw IllegalStateException("Only a single call to use() is allowed per @Test method") + throw IllegalStateException( + "Only a single call to use() is allowed per @Test method" + ) } else -> { @@ -150,9 +159,7 @@ internal constructor( override fun beforeEach(context: ExtensionContext) { state = STATE_INIT - environment = AndroidComposeUiTestEnvironment { - getActivityFromScenario(scenario) - } + environment = AndroidComposeUiTestEnvironment { getActivityFromScenario(scenario) } } /* BeforeTestExecutionCallback */ @@ -181,14 +188,14 @@ internal constructor( override fun supportsParameter( parameterContext: ParameterContext, - extensionContext: ExtensionContext + extensionContext: ExtensionContext, ): Boolean { return ComposeExtension::class.java.isAssignableFrom(parameterContext.parameter.type) } override fun resolveParameter( parameterContext: ParameterContext, - extensionContext: ExtensionContext + extensionContext: ExtensionContext, ): Any { return this } diff --git a/instrumentation/compose/src/main/java/de/mannodermaus/junit5/compose/ComposeContext.kt b/instrumentation/compose/src/main/java/de/mannodermaus/junit5/compose/ComposeContext.kt index 7d8e1f66..edc206a8 100644 --- a/instrumentation/compose/src/main/java/de/mannodermaus/junit5/compose/ComposeContext.kt +++ b/instrumentation/compose/src/main/java/de/mannodermaus/junit5/compose/ComposeContext.kt @@ -15,48 +15,58 @@ import androidx.compose.ui.test.waitUntilExactlyOneExists import androidx.compose.ui.test.waitUntilNodeCount import androidx.compose.ui.unit.Density -/** - * A context through which composable blocks can be orchestrated within a [ComposeExtension]. - */ +/** A context through which composable blocks can be orchestrated within a [ComposeExtension]. */ public sealed interface ComposeContext : SemanticsNodeInteractionsProvider { // Internal note: The below method list is a copy of `ComposeContentTestRule`, // preventing the viral spread of its ExperimentalTestApi annotation // into the consumer's codebase and separating it from JUnit 4's TestRule public val density: Density public val mainClock: MainTestClock + public fun runOnUiThread(action: () -> T): T + public fun runOnIdle(action: () -> T): T + public fun waitForIdle() + public suspend fun awaitIdle() + public fun waitUntil(timeoutMillis: Long = 1_000L, condition: () -> Boolean) + public fun waitUntil( conditionDescription: String, timeoutMillis: Long = 1_000L, - condition: () -> Boolean + condition: () -> Boolean, ) public fun waitUntilNodeCount( matcher: SemanticsMatcher, count: Int, - timeoutMillis: Long = 1_000L + timeoutMillis: Long = 1_000L, ) public fun waitUntilAtLeastOneExists(matcher: SemanticsMatcher, timeoutMillis: Long = 1_000L) + public fun waitUntilExactlyOneExists(matcher: SemanticsMatcher, timeoutMillis: Long = 1_000L) + public fun waitUntilDoesNotExist(matcher: SemanticsMatcher, timeoutMillis: Long = 1_000L) + public fun registerIdlingResource(idlingResource: IdlingResource) + public fun unregisterIdlingResource(idlingResource: IdlingResource) + public fun setContent(composable: @Composable () -> Unit) } @OptIn(ExperimentalTestApi::class) -internal class ComposeContextImpl( - private val delegate: ComposeUiTest -) : ComposeContext, SemanticsNodeInteractionsProvider by delegate { +internal class ComposeContextImpl(private val delegate: ComposeUiTest) : + ComposeContext, SemanticsNodeInteractionsProvider by delegate { - override val density: Density get() = delegate.density + override val density: Density + get() = delegate.density - override val mainClock: MainTestClock get() = delegate.mainClock + override val mainClock: MainTestClock + get() = delegate.mainClock override fun runOnUiThread(action: () -> T): T = delegate.runOnUiThread(action) @@ -72,7 +82,7 @@ internal class ComposeContextImpl( override fun waitUntil( conditionDescription: String, timeoutMillis: Long, - condition: () -> Boolean + condition: () -> Boolean, ) { delegate.waitUntil(conditionDescription, timeoutMillis, condition) } @@ -97,12 +107,12 @@ internal class ComposeContextImpl( override fun onNode( matcher: SemanticsMatcher, - useUnmergedTree: Boolean + useUnmergedTree: Boolean, ): SemanticsNodeInteraction = delegate.onNode(matcher, useUnmergedTree) override fun onAllNodes( matcher: SemanticsMatcher, - useUnmergedTree: Boolean + useUnmergedTree: Boolean, ): SemanticsNodeInteractionCollection = delegate.onAllNodes(matcher, useUnmergedTree) override fun setContent(composable: @Composable () -> Unit) = delegate.setContent(composable) diff --git a/instrumentation/compose/src/main/java/de/mannodermaus/junit5/compose/ComposeExtension.kt b/instrumentation/compose/src/main/java/de/mannodermaus/junit5/compose/ComposeExtension.kt index a99e259d..abf277ee 100644 --- a/instrumentation/compose/src/main/java/de/mannodermaus/junit5/compose/ComposeExtension.kt +++ b/instrumentation/compose/src/main/java/de/mannodermaus/junit5/compose/ComposeExtension.kt @@ -5,25 +5,25 @@ import org.junit.jupiter.api.extension.ExtendWith import org.junit.jupiter.api.extension.Extension /** - * A JUnit 5 [Extension] that allows you to test and control [Composable]s and application using Compose. - * The functionality of testing Compose is provided by means of the [runComposeTest] method, - * which receives a [ComposeContext] from which the test can be orchestrated. The test will block - * until the app or composable is idle, to ensure the tests are deterministic. + * A JUnit 5 [Extension] that allows you to test and control [Composable]s and application using + * Compose. The functionality of testing Compose is provided by means of the [runComposeTest] + * method, which receives a [ComposeContext] from which the test can be orchestrated. The test will + * block until the app or composable is idle, to ensure the tests are deterministic. * - * This extension can be added to any JUnit 5 class using the [ExtendWith] annotation, - * or registered globally through a configuration file. Alternatively, you can instantiate the extension - * in a field within the test class using any of the [createComposeExtension] or + * This extension can be added to any JUnit 5 class using the [ExtendWith] annotation, or registered + * globally through a configuration file. Alternatively, you can instantiate the extension in a + * field within the test class using any of the [createComposeExtension] or * [createAndroidComposeExtension] factory methods. */ public interface ComposeExtension : Extension { /** - * Set up and drive the execution of a Compose test within the provided [block]. - * Depending on the time this is called, it will either queue up a preparatory action for the test - * (e.g. in @BeforeEach) - * The receive of this block is a [ComposeContext], through which you can access all sorts of - * utilities to drive the execution of the test, such as driving the clock or executing actions - * on the UI thread. The main purpose is provided through [ComposeContext.setContent], however: - * With this function, you can pass an arbitrary composable tree to the extension and evaluate it afterwards. + * Set up and drive the execution of a Compose test within the provided [block]. Depending on + * the time this is called, it will either queue up a preparatory action for the test (e.g. + * in @BeforeEach) The receive of this block is a [ComposeContext], through which you can access + * all sorts of utilities to drive the execution of the test, such as driving the clock or + * executing actions on the UI thread. The main purpose is provided through + * [ComposeContext.setContent], however: With this function, you can pass an arbitrary + * composable tree to the extension and evaluate it afterwards. */ public fun use(block: ComposeContext.() -> Unit) } diff --git a/instrumentation/compose/src/test/java/de/mannodermaus/junit5/compose/ComposeContextTests.kt b/instrumentation/compose/src/test/java/de/mannodermaus/junit5/compose/ComposeContextTests.kt index eed1072f..670dd7fb 100644 --- a/instrumentation/compose/src/test/java/de/mannodermaus/junit5/compose/ComposeContextTests.kt +++ b/instrumentation/compose/src/test/java/de/mannodermaus/junit5/compose/ComposeContextTests.kt @@ -2,10 +2,10 @@ package de.mannodermaus.junit5.compose import androidx.compose.ui.test.junit4.ComposeContentTestRule import androidx.compose.ui.test.junit4.ComposeTestRule +import java.lang.reflect.Method import org.junit.jupiter.api.Assertions.fail import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.MethodSource -import java.lang.reflect.Method class ComposeContextTests { companion object { diff --git a/instrumentation/core/build.gradle.kts b/instrumentation/core/build.gradle.kts index df4cfb17..bc39abe8 100644 --- a/instrumentation/core/build.gradle.kts +++ b/instrumentation/core/build.gradle.kts @@ -1,5 +1,3 @@ -import java.util.concurrent.atomic.AtomicBoolean - plugins { alias(libs.plugins.android.junit) alias(libs.plugins.android.library) diff --git a/instrumentation/core/src/androidTest/java/de/mannodermaus/junit5/BaselineJUnit4Test.kt b/instrumentation/core/src/androidTest/java/de/mannodermaus/junit5/BaselineJUnit4Test.kt index dfe1c9d0..22f38b97 100644 --- a/instrumentation/core/src/androidTest/java/de/mannodermaus/junit5/BaselineJUnit4Test.kt +++ b/instrumentation/core/src/androidTest/java/de/mannodermaus/junit5/BaselineJUnit4Test.kt @@ -4,14 +4,13 @@ import org.junit.Assert.assertEquals import org.junit.Test /** - * This JUnit 4 test class is here to validate - * that older device, which ignore JUnit 5 tests, - * still execute older ones. + * This JUnit 4 test class is here to validate that older device, which ignore JUnit 5 tests, still + * execute older ones. */ class BaselineJUnit4Test { - @Test - fun run() { - assertEquals(4, 2 + 2) - } + @Test + fun run() { + assertEquals(4, 2 + 2) + } } diff --git a/instrumentation/core/src/androidTest/java/de/mannodermaus/junit5/KotlinInstrumentationTests.kt b/instrumentation/core/src/androidTest/java/de/mannodermaus/junit5/KotlinInstrumentationTests.kt index 4f8125a5..8ca8c76d 100644 --- a/instrumentation/core/src/androidTest/java/de/mannodermaus/junit5/KotlinInstrumentationTests.kt +++ b/instrumentation/core/src/androidTest/java/de/mannodermaus/junit5/KotlinInstrumentationTests.kt @@ -18,50 +18,33 @@ import org.junit.jupiter.params.provider.ValueSource internal class KotlinInstrumentationTests { - @JvmField - @RegisterExtension - val scenarioExtension = ActivityScenarioExtension.launch() + @JvmField + @RegisterExtension + val scenarioExtension = ActivityScenarioExtension.launch() - @Test - fun testUsingGetScenario() { - val scenario = scenarioExtension.scenario - onView(withText("TestActivity")).check(matches(isDisplayed())) - scenario.onActivity { it.changeText("New Text") } - onView(withText("New Text")).check(matches(isDisplayed())) - } + @Test + fun testUsingGetScenario() { + val scenario = scenarioExtension.scenario + onView(withText("TestActivity")).check(matches(isDisplayed())) + scenario.onActivity { it.changeText("New Text") } + onView(withText("New Text")).check(matches(isDisplayed())) + } - @DisplayName("cool display name") - @Test - fun testWithDisplayName() { - } + @DisplayName("cool display name") @Test fun testWithDisplayName() {} - @Test - fun testUsingMethodParameter(scenario: ActivityScenario) { - onView(withText("TestActivity")).check(matches(isDisplayed())) - scenario.onActivity { it.changeText("New Text") } - onView(withText("New Text")).check(matches(isDisplayed())) - } + @Test + fun testUsingMethodParameter(scenario: ActivityScenario) { + onView(withText("TestActivity")).check(matches(isDisplayed())) + scenario.onActivity { it.changeText("New Text") } + onView(withText("New Text")).check(matches(isDisplayed())) + } - @ParameterizedTest - @ValueSource(ints = [1, 4, 6, 7]) - fun kotlinTestWithParameters(value: Int) { + @ParameterizedTest @ValueSource(ints = [1, 4, 6, 7]) fun kotlinTestWithParameters(value: Int) {} - } + @RepeatedTest(3) fun kotlinRepeatedTest(info: RepetitionInfo) {} - @RepeatedTest(3) - fun kotlinRepeatedTest(info: RepetitionInfo) { + @TestFactory + fun kotlinTestFactory() = listOf(dynamicTest("Dynamic 1") {}, dynamicTest("Dynamic 2") {}) - } - - @TestFactory - fun kotlinTestFactory() = listOf( - dynamicTest("Dynamic 1") {}, - dynamicTest("Dynamic 2") {} - ) - - @EnabledOnManufacturer(["Samsung"]) - @Test - fun onlyOnSamsung() { - - } + @EnabledOnManufacturer(["Samsung"]) @Test fun onlyOnSamsung() {} } diff --git a/instrumentation/core/src/androidTest/java/de/mannodermaus/junit5/TaggedTests.kt b/instrumentation/core/src/androidTest/java/de/mannodermaus/junit5/TaggedTests.kt index ea5a7508..00e308a8 100644 --- a/instrumentation/core/src/androidTest/java/de/mannodermaus/junit5/TaggedTests.kt +++ b/instrumentation/core/src/androidTest/java/de/mannodermaus/junit5/TaggedTests.kt @@ -5,9 +5,7 @@ import org.junit.jupiter.api.Tag import org.junit.jupiter.api.Test class TaggedTests { - @Test - fun includedTest() { - } + @Test fun includedTest() {} @Tag("nope") @Test diff --git a/instrumentation/core/src/androidTest/java/de/mannodermaus/junit5/inheritance/KotlinInheritanceTests.kt b/instrumentation/core/src/androidTest/java/de/mannodermaus/junit5/inheritance/KotlinInheritanceTests.kt index a0fd3ea7..5a26e131 100644 --- a/instrumentation/core/src/androidTest/java/de/mannodermaus/junit5/inheritance/KotlinInheritanceTests.kt +++ b/instrumentation/core/src/androidTest/java/de/mannodermaus/junit5/inheritance/KotlinInheritanceTests.kt @@ -31,5 +31,6 @@ class KotlinInterfaceTest : KotlinInterface { class KotlinMixedInterfaceTest : KotlinInterface, JavaInterface { override val kotlinValue: Int = 1337 + override fun getJavaValue(): Long = 1234L } diff --git a/instrumentation/core/src/androidTest/java/de/mannodermaus/junit5/otherpackage/AnotherTest.kt b/instrumentation/core/src/androidTest/java/de/mannodermaus/junit5/otherpackage/AnotherTest.kt index cee98af4..513a6bda 100644 --- a/instrumentation/core/src/androidTest/java/de/mannodermaus/junit5/otherpackage/AnotherTest.kt +++ b/instrumentation/core/src/androidTest/java/de/mannodermaus/junit5/otherpackage/AnotherTest.kt @@ -3,7 +3,5 @@ package de.mannodermaus.junit5.otherpackage import org.junit.jupiter.api.Test class AnotherTest { - @Test - fun anotherTest() { - } + @Test fun anotherTest() {} } diff --git a/instrumentation/core/src/debug/java/de/mannodermaus/junit5/TestActivity.kt b/instrumentation/core/src/debug/java/de/mannodermaus/junit5/TestActivity.kt index bf6ced5e..cdcff9d5 100644 --- a/instrumentation/core/src/debug/java/de/mannodermaus/junit5/TestActivity.kt +++ b/instrumentation/core/src/debug/java/de/mannodermaus/junit5/TestActivity.kt @@ -6,14 +6,14 @@ import android.widget.TextView internal class TestActivity : Activity() { - private val textView by lazy { findViewById(R.id.textView) } + private val textView by lazy { findViewById(R.id.textView) } - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_test) - } + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_test) + } - fun changeText(label: String) { - textView.text = label - } + fun changeText(label: String) { + textView.text = label + } } diff --git a/instrumentation/core/src/five/kotlin/de/mannodermaus/junit5/CoreConstants.kt b/instrumentation/core/src/five/kotlin/de/mannodermaus/junit5/CoreConstants.kt index b55eb448..3812b611 100644 --- a/instrumentation/core/src/five/kotlin/de/mannodermaus/junit5/CoreConstants.kt +++ b/instrumentation/core/src/five/kotlin/de/mannodermaus/junit5/CoreConstants.kt @@ -1,7 +1,7 @@ package de.mannodermaus.junit5 /** - * The minimum Android API level on which JUnit Framework tests may be executed. - * Trying to launch a test on an older device will simply mark it as 'skipped'. + * The minimum Android API level on which JUnit Framework tests may be executed. Trying to launch a + * test on an older device will simply mark it as 'skipped'. */ public const val JUNIT_FRAMEWORK_MINIMUM_SDK_VERSION: Int = 26 diff --git a/instrumentation/core/src/five/kotlin/de/mannodermaus/junit5/internal/compat/ExtensionContextCompat.kt b/instrumentation/core/src/five/kotlin/de/mannodermaus/junit5/internal/compat/ExtensionContextCompat.kt index 98bd1096..0a2d9cdd 100644 --- a/instrumentation/core/src/five/kotlin/de/mannodermaus/junit5/internal/compat/ExtensionContextCompat.kt +++ b/instrumentation/core/src/five/kotlin/de/mannodermaus/junit5/internal/compat/ExtensionContextCompat.kt @@ -1,18 +1,18 @@ package de.mannodermaus.junit5.internal.compat -import org.junit.jupiter.api.extension.ExtensionContext import kotlin.reflect.KClass +import org.junit.jupiter.api.extension.ExtensionContext // JUnit 5 facade of ExtensionContext.Store APIs // that were deprecated/removed in subsequent versions of the framework. internal fun ExtensionContext.Store.computeIfAbsentCompat( key: K, - defaultCreator: (K) -> V + defaultCreator: (K) -> V, ): Any = getOrComputeIfAbsent(key, defaultCreator) internal fun ExtensionContext.Store.computeIfAbsentCompat( key: K, defaultCreator: (K) -> V, - requiredType: KClass + requiredType: KClass, ): V = getOrComputeIfAbsent(key, defaultCreator, requiredType.java) diff --git a/instrumentation/core/src/main/java/de/mannodermaus/junit5/ActivityScenarioExtension.kt b/instrumentation/core/src/main/java/de/mannodermaus/junit5/ActivityScenarioExtension.kt index a4936c32..0912b31a 100644 --- a/instrumentation/core/src/main/java/de/mannodermaus/junit5/ActivityScenarioExtension.kt +++ b/instrumentation/core/src/main/java/de/mannodermaus/junit5/ActivityScenarioExtension.kt @@ -8,6 +8,8 @@ import androidx.test.core.app.ActivityScenario import de.mannodermaus.junit5.ActivityScenarioExtension.Companion.launch import de.mannodermaus.junit5.internal.LOG_TAG import de.mannodermaus.junit5.internal.compat.computeIfAbsentCompat +import java.lang.reflect.ParameterizedType +import java.util.concurrent.locks.ReentrantLock import org.junit.jupiter.api.extension.AfterEachCallback import org.junit.jupiter.api.extension.BeforeEachCallback import org.junit.jupiter.api.extension.ExtensionContext @@ -15,20 +17,17 @@ import org.junit.jupiter.api.extension.ParameterContext import org.junit.jupiter.api.extension.ParameterResolver import org.junit.jupiter.api.extension.RegisterExtension import org.junit.jupiter.api.parallel.ExecutionMode -import java.lang.reflect.ParameterizedType -import java.util.concurrent.locks.ReentrantLock /** - * JUnit 5 Extension for the [ActivityScenario] API, - * provided by the AndroidX test core library. + * JUnit 5 Extension for the [ActivityScenario] API, provided by the AndroidX test core library. * - * This extension is used in lieu of a JUnit 4 Rule to automatically - * launch/stop an [ActivityScenario] for each test case. + * This extension is used in lieu of a JUnit 4 Rule to automatically launch/stop an + * [ActivityScenario] for each test case. * - * To use this extension in your test class, add it as a non-private instance field - * to your test class, using one of the factory methods named [launch]. - * Then, annotate this field with JUnit Jupiter's [RegisterExtension] annotation. - * In Kotlin, also add [JvmField] or else the generated property won't be visible to the TestEngine! + * To use this extension in your test class, add it as a non-private instance field to your test + * class, using one of the factory methods named [launch]. Then, annotate this field with JUnit + * Jupiter's [RegisterExtension] annotation. In Kotlin, also add [JvmField] or else the generated + * property won't be visible to the TestEngine! * * ``` * // Java @@ -50,7 +49,6 @@ import java.util.concurrent.locks.ReentrantLock * In your test method, you can obtain a reference to the scenario in two ways: * * A) You obtain it from the extension directly through its accessor method: - * * ``` * // Java * class MyActivityTests { @@ -81,7 +79,6 @@ import java.util.concurrent.locks.ReentrantLock * ``` * * B) You add a parameter of type [ActivityScenario], with the activity class as its generic type: - * * ``` * // Java * class MyActivityTests { @@ -108,41 +105,44 @@ import java.util.concurrent.locks.ReentrantLock * } * } * ``` - * */ @RequiresApi(26) public class ActivityScenarioExtension -private constructor(private val scenarioSupplier: () -> ActivityScenario) : BeforeEachCallback, - AfterEachCallback, ParameterResolver { +private constructor(private val scenarioSupplier: () -> ActivityScenario) : + BeforeEachCallback, AfterEachCallback, ParameterResolver { public companion object { private const val WARNING_KEY = "de.mannodermaus.junit5.LogConcurrentExecutionWarning" private const val LOCK_KEY = "de.mannodermaus.junit5.SharedResourceLock" - private val NAMESPACE = - ExtensionContext.Namespace.create(ActivityScenarioExtension::class) + private val NAMESPACE = ExtensionContext.Namespace.create(ActivityScenarioExtension::class) /** - * Launches an activity of a given class and constructs an [ActivityScenario] for it. - * A default launch intent without specific extras is used to launch the activity. + * Launches an activity of a given class and constructs an [ActivityScenario] for it. A + * default launch intent without specific extras is used to launch the activity. */ @JvmStatic public fun launch(activityClass: Class): ActivityScenarioExtension = - ActivityScenarioExtension { ActivityScenario.launch(activityClass) } + ActivityScenarioExtension { + ActivityScenario.launch(activityClass) + } /** - * Launches an activity of a given class and constructs an [ActivityScenario] for it. - * The given intent is used to launch the activity. + * Launches an activity of a given class and constructs an [ActivityScenario] for it. The + * given intent is used to launch the activity. */ @JvmStatic - public fun launch(startActivityIntent: Intent): ActivityScenarioExtension = - ActivityScenarioExtension { ActivityScenario.launch(startActivityIntent) } + public fun launch( + startActivityIntent: Intent + ): ActivityScenarioExtension = ActivityScenarioExtension { + ActivityScenario.launch(startActivityIntent) + } /* Kotlin-specific convenience variations */ /** - * Launches an activity of a given class and constructs an [ActivityScenario] for it. - * A default launch intent without specific extras is used to launch the activity. + * Launches an activity of a given class and constructs an [ActivityScenario] for it. A + * default launch intent without specific extras is used to launch the activity. */ public inline fun launch(): ActivityScenarioExtension = launch(A::class.java) @@ -154,6 +154,7 @@ private constructor(private val scenarioSupplier: () -> ActivityScenario) : B /** * Returns the current [ActivityScenario] of the activity class. + * * @throws NullPointerException If this method is called while no test is running */ public val scenario: ActivityScenario @@ -179,17 +180,16 @@ private constructor(private val scenarioSupplier: () -> ActivityScenario) : B override fun supportsParameter( parameterContext: ParameterContext, - extensionContext: ExtensionContext + extensionContext: ExtensionContext, ): Boolean { // The extension can resolve ActivityScenario parameters that use the correct activity type. val paramType = parameterContext.parameter.parameterizedType - return paramType is ParameterizedType - && paramType.rawType == ActivityScenario::class.java + return paramType is ParameterizedType && paramType.rawType == ActivityScenario::class.java } override fun resolveParameter( parameterContext: ParameterContext, - extensionContext: ExtensionContext + extensionContext: ExtensionContext, ): Any = scenario /* Private */ @@ -208,11 +208,12 @@ private constructor(private val scenarioSupplier: () -> ActivityScenario) : B // Create a global lock for restricting test execution to one-by-one; // this is necessary to ensure that only one ActivityScenario is ever active at a time, // preventing violations of Android's instrumentation and Espresso - val lock = store.computeIfAbsentCompat( - key = LOCK_KEY, - defaultCreator = { ReentrantLock() }, - requiredType = ReentrantLock::class, - ) + val lock = + store.computeIfAbsentCompat( + key = LOCK_KEY, + defaultCreator = { ReentrantLock() }, + requiredType = ReentrantLock::class, + ) if (state) { lock.lock() @@ -224,16 +225,15 @@ private constructor(private val scenarioSupplier: () -> ActivityScenario) : B private fun logConcurrentExecutionWarningOnce(store: ExtensionContext.Store) { store.computeIfAbsentCompat(WARNING_KEY) { setOf( - " [WARNING!] UI tests using ActivityScenarioExtension should not be executed in CONCURRENT mode.", - " We will try to disable parallelism for Espresso tests, but this may be error-prone", - " (also, your execution times will look off). If you encounter issues, please consider", - " annotating your Espresso test classes to use the SAME_THREAD mode via the @Execution annotation!", - " --------------------------------------------------------------------", - " For more information, feel free to consult the JUnit 5 User Guide at:", - " https://junit.org/junit5/docs/current/user-guide/#writing-tests-parallel-execution-synchronization", - ).forEach { line -> - Log.e(LOG_TAG, line) - } + " [WARNING!] UI tests using ActivityScenarioExtension should not be executed in CONCURRENT mode.", + " We will try to disable parallelism for Espresso tests, but this may be error-prone", + " (also, your execution times will look off). If you encounter issues, please consider", + " annotating your Espresso test classes to use the SAME_THREAD mode via the @Execution annotation!", + " --------------------------------------------------------------------", + " For more information, feel free to consult the JUnit 5 User Guide at:", + " https://junit.org/junit5/docs/current/user-guide/#writing-tests-parallel-execution-synchronization", + ) + .forEach { line -> Log.e(LOG_TAG, line) } } } } diff --git a/instrumentation/core/src/main/java/de/mannodermaus/junit5/DeprecatedCoreConstants.kt b/instrumentation/core/src/main/java/de/mannodermaus/junit5/DeprecatedCoreConstants.kt index 0efc4a31..1f1bc528 100644 --- a/instrumentation/core/src/main/java/de/mannodermaus/junit5/DeprecatedCoreConstants.kt +++ b/instrumentation/core/src/main/java/de/mannodermaus/junit5/DeprecatedCoreConstants.kt @@ -4,6 +4,6 @@ package de.mannodermaus.junit5 @Deprecated( message = "Renamed to JUNIT_FRAMEWORK_MINIMUM_SDK_VERSION", - replaceWith = ReplaceWith("JUNIT_FRAMEWORK_MINIMUM_SDK_VERSION") + replaceWith = ReplaceWith("JUNIT_FRAMEWORK_MINIMUM_SDK_VERSION"), ) public const val JUNIT5_MINIMUM_SDK_VERSION: Int = JUNIT_FRAMEWORK_MINIMUM_SDK_VERSION diff --git a/instrumentation/core/src/main/java/de/mannodermaus/junit5/condition/DisabledIfBuildConfigValue.kt b/instrumentation/core/src/main/java/de/mannodermaus/junit5/condition/DisabledIfBuildConfigValue.kt index 61c410d4..faa07a8a 100644 --- a/instrumentation/core/src/main/java/de/mannodermaus/junit5/condition/DisabledIfBuildConfigValue.kt +++ b/instrumentation/core/src/main/java/de/mannodermaus/junit5/condition/DisabledIfBuildConfigValue.kt @@ -6,7 +6,4 @@ import org.junit.jupiter.api.extension.ExtendWith @Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION) @Retention(AnnotationRetention.RUNTIME) @ExtendWith(DisabledIfBuildConfigValueCondition::class) -public annotation class DisabledIfBuildConfigValue( - val named: String, - val matches: String -) +public annotation class DisabledIfBuildConfigValue(val named: String, val matches: String) diff --git a/instrumentation/core/src/main/java/de/mannodermaus/junit5/condition/DisabledOnManufacturer.kt b/instrumentation/core/src/main/java/de/mannodermaus/junit5/condition/DisabledOnManufacturer.kt index 06d24683..257acc10 100644 --- a/instrumentation/core/src/main/java/de/mannodermaus/junit5/condition/DisabledOnManufacturer.kt +++ b/instrumentation/core/src/main/java/de/mannodermaus/junit5/condition/DisabledOnManufacturer.kt @@ -8,5 +8,5 @@ import org.junit.jupiter.api.extension.ExtendWith @ExtendWith(DisabledOnManufacturerCondition::class) public annotation class DisabledOnManufacturer( val value: Array, - val ignoreCase: Boolean = true + val ignoreCase: Boolean = true, ) diff --git a/instrumentation/core/src/main/java/de/mannodermaus/junit5/condition/DisabledOnSdkVersion.kt b/instrumentation/core/src/main/java/de/mannodermaus/junit5/condition/DisabledOnSdkVersion.kt index 0434575a..44b58203 100644 --- a/instrumentation/core/src/main/java/de/mannodermaus/junit5/condition/DisabledOnSdkVersion.kt +++ b/instrumentation/core/src/main/java/de/mannodermaus/junit5/condition/DisabledOnSdkVersion.kt @@ -11,5 +11,5 @@ import org.junit.jupiter.api.extension.ExtendWith @ExtendWith(DisabledOnSdkVersionCondition::class) public annotation class DisabledOnSdkVersion( @IntRange(from = JUNIT_FRAMEWORK_MINIMUM_SDK_VERSION.toLong()) val from: Int = NOT_SET, - @IntRange(from = JUNIT_FRAMEWORK_MINIMUM_SDK_VERSION.toLong()) val until: Int = NOT_SET + @IntRange(from = JUNIT_FRAMEWORK_MINIMUM_SDK_VERSION.toLong()) val until: Int = NOT_SET, ) diff --git a/instrumentation/core/src/main/java/de/mannodermaus/junit5/condition/EnabledIfBuildConfigValue.kt b/instrumentation/core/src/main/java/de/mannodermaus/junit5/condition/EnabledIfBuildConfigValue.kt index 03822d13..6ccb01ac 100644 --- a/instrumentation/core/src/main/java/de/mannodermaus/junit5/condition/EnabledIfBuildConfigValue.kt +++ b/instrumentation/core/src/main/java/de/mannodermaus/junit5/condition/EnabledIfBuildConfigValue.kt @@ -6,7 +6,4 @@ import org.junit.jupiter.api.extension.ExtendWith @Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION) @Retention(AnnotationRetention.RUNTIME) @ExtendWith(EnabledIfBuildConfigValueCondition::class) -public annotation class EnabledIfBuildConfigValue( - val named: String, - val matches: String -) +public annotation class EnabledIfBuildConfigValue(val named: String, val matches: String) diff --git a/instrumentation/core/src/main/java/de/mannodermaus/junit5/condition/EnabledOnManufacturer.kt b/instrumentation/core/src/main/java/de/mannodermaus/junit5/condition/EnabledOnManufacturer.kt index 80487978..994f59ca 100644 --- a/instrumentation/core/src/main/java/de/mannodermaus/junit5/condition/EnabledOnManufacturer.kt +++ b/instrumentation/core/src/main/java/de/mannodermaus/junit5/condition/EnabledOnManufacturer.kt @@ -8,5 +8,5 @@ import org.junit.jupiter.api.extension.ExtendWith @ExtendWith(EnabledOnManufacturerCondition::class) public annotation class EnabledOnManufacturer( val value: Array, - val ignoreCase: Boolean = true + val ignoreCase: Boolean = true, ) diff --git a/instrumentation/core/src/main/java/de/mannodermaus/junit5/condition/EnabledOnSdkVersion.kt b/instrumentation/core/src/main/java/de/mannodermaus/junit5/condition/EnabledOnSdkVersion.kt index 09878c9c..82b64afa 100644 --- a/instrumentation/core/src/main/java/de/mannodermaus/junit5/condition/EnabledOnSdkVersion.kt +++ b/instrumentation/core/src/main/java/de/mannodermaus/junit5/condition/EnabledOnSdkVersion.kt @@ -11,5 +11,5 @@ import org.junit.jupiter.api.extension.ExtendWith @ExtendWith(EnabledOnSdkVersionCondition::class) public annotation class EnabledOnSdkVersion( @IntRange(from = JUNIT_FRAMEWORK_MINIMUM_SDK_VERSION.toLong()) val from: Int = NOT_SET, - @IntRange(from = JUNIT_FRAMEWORK_MINIMUM_SDK_VERSION.toLong()) val until: Int = NOT_SET + @IntRange(from = JUNIT_FRAMEWORK_MINIMUM_SDK_VERSION.toLong()) val until: Int = NOT_SET, ) diff --git a/instrumentation/core/src/main/java/de/mannodermaus/junit5/internal/DisabledIfBuildConfigValueCondition.kt b/instrumentation/core/src/main/java/de/mannodermaus/junit5/internal/DisabledIfBuildConfigValueCondition.kt index 12dd30c3..6943128c 100644 --- a/instrumentation/core/src/main/java/de/mannodermaus/junit5/internal/DisabledIfBuildConfigValueCondition.kt +++ b/instrumentation/core/src/main/java/de/mannodermaus/junit5/internal/DisabledIfBuildConfigValueCondition.kt @@ -1,8 +1,8 @@ package de.mannodermaus.junit5.internal import androidx.annotation.RequiresApi -import de.mannodermaus.junit5.internal.utils.BuildConfigValueUtils import de.mannodermaus.junit5.condition.DisabledIfBuildConfigValue +import de.mannodermaus.junit5.internal.utils.BuildConfigValueUtils import org.junit.jupiter.api.extension.ConditionEvaluationResult import org.junit.jupiter.api.extension.ConditionEvaluationResult.disabled import org.junit.jupiter.api.extension.ConditionEvaluationResult.enabled @@ -14,8 +14,7 @@ import org.junit.platform.commons.util.Preconditions internal class DisabledIfBuildConfigValueCondition : ExecutionCondition { companion object { - private val ENABLED_BY_DEFAULT = - enabled("@DisabledIfBuildConfigValue is not present") + private val ENABLED_BY_DEFAULT = enabled("@DisabledIfBuildConfigValue is not present") } @RequiresApi(24) @@ -27,16 +26,25 @@ internal class DisabledIfBuildConfigValueCondition : ExecutionCondition { val name = annotation.named.trim() val regexString = annotation.matches - Preconditions.notBlank(name) { "The 'named' attribute must not be blank in $annotation" } - Preconditions.notBlank(regexString) { "The 'matches' attribute must not be blank in $annotation" } + Preconditions.notBlank(name) { + "The 'named' attribute must not be blank in $annotation" + } + Preconditions.notBlank(regexString) { + "The 'matches' attribute must not be blank in $annotation" + } - val actual = runCatching { BuildConfigValueUtils.getAsString(name) }.getOrNull() - ?: return enabled("BuildConfig key [$name] does not exist") + val actual = + runCatching { BuildConfigValueUtils.getAsString(name) }.getOrNull() + ?: return enabled("BuildConfig key [$name] does not exist") return if (actual.matches(regexString.toRegex())) { - disabled("BuildConfig key [$name] with value [$actual] matches regular expression [$regexString]") + disabled( + "BuildConfig key [$name] with value [$actual] matches regular expression [$regexString]" + ) } else { - enabled("BuildConfig key [$name] with value [$actual] does not match regular expression [$regexString]") + enabled( + "BuildConfig key [$name] with value [$actual] does not match regular expression [$regexString]" + ) } } diff --git a/instrumentation/core/src/main/java/de/mannodermaus/junit5/internal/DisabledOnManufacturerCondition.kt b/instrumentation/core/src/main/java/de/mannodermaus/junit5/internal/DisabledOnManufacturerCondition.kt index dd7a1ffb..118e6f49 100644 --- a/instrumentation/core/src/main/java/de/mannodermaus/junit5/internal/DisabledOnManufacturerCondition.kt +++ b/instrumentation/core/src/main/java/de/mannodermaus/junit5/internal/DisabledOnManufacturerCondition.kt @@ -1,7 +1,7 @@ package de.mannodermaus.junit5.internal -import androidx.annotation.RequiresApi import android.os.Build +import androidx.annotation.RequiresApi import de.mannodermaus.junit5.condition.DisabledOnManufacturer import de.mannodermaus.junit5.internal.EnabledOnManufacturerCondition.Companion.disabled import de.mannodermaus.junit5.internal.EnabledOnManufacturerCondition.Companion.enabled @@ -29,7 +29,7 @@ internal class DisabledOnManufacturerCondition : ExecutionCondition { Preconditions.condition( patterns.isNotEmpty(), - "You must declare at least one Manufacturer in @DisabledOnManufacturer" + "You must declare at least one Manufacturer in @DisabledOnManufacturer", ) return if (patterns.any { Build.MANUFACTURER.equals(it, ignoreCase = ignoreCase) }) { diff --git a/instrumentation/core/src/main/java/de/mannodermaus/junit5/internal/DisabledOnSdkVersionCondition.kt b/instrumentation/core/src/main/java/de/mannodermaus/junit5/internal/DisabledOnSdkVersionCondition.kt index 649a0093..eb013fc0 100644 --- a/instrumentation/core/src/main/java/de/mannodermaus/junit5/internal/DisabledOnSdkVersionCondition.kt +++ b/instrumentation/core/src/main/java/de/mannodermaus/junit5/internal/DisabledOnSdkVersionCondition.kt @@ -1,7 +1,7 @@ package de.mannodermaus.junit5.internal -import androidx.annotation.RequiresApi import android.os.Build +import androidx.annotation.RequiresApi import de.mannodermaus.junit5.condition.DisabledOnSdkVersion import de.mannodermaus.junit5.internal.EnabledOnSdkVersionCondition.Companion.disabled import de.mannodermaus.junit5.internal.EnabledOnSdkVersionCondition.Companion.enabled @@ -30,11 +30,12 @@ internal class DisabledOnSdkVersionCondition : ExecutionCondition { val hasUpperBound = untilApi != NOT_SET Preconditions.condition( hasLowerBound || hasUpperBound, - "At least one value must be provided in @DisabledOnSdkVersion" + "At least one value must be provided in @DisabledOnSdkVersion", ) // Constrain the current API Level based on the presence of "fromApi" & "untilApi": - // If either one is not set at all, that part of the conditional becomes true automatically + // If either one is not set at all, that part of the conditional becomes true + // automatically val lowerCheck = !hasLowerBound || Build.VERSION.SDK_INT >= fromApi val upperCheck = !hasUpperBound || Build.VERSION.SDK_INT <= untilApi return if (lowerCheck && upperCheck) { diff --git a/instrumentation/core/src/main/java/de/mannodermaus/junit5/internal/EnabledIfBuildConfigValueCondition.kt b/instrumentation/core/src/main/java/de/mannodermaus/junit5/internal/EnabledIfBuildConfigValueCondition.kt index 07e15f35..02ed4505 100644 --- a/instrumentation/core/src/main/java/de/mannodermaus/junit5/internal/EnabledIfBuildConfigValueCondition.kt +++ b/instrumentation/core/src/main/java/de/mannodermaus/junit5/internal/EnabledIfBuildConfigValueCondition.kt @@ -1,8 +1,8 @@ package de.mannodermaus.junit5.internal import androidx.annotation.RequiresApi -import de.mannodermaus.junit5.internal.utils.BuildConfigValueUtils import de.mannodermaus.junit5.condition.EnabledIfBuildConfigValue +import de.mannodermaus.junit5.internal.utils.BuildConfigValueUtils import org.junit.jupiter.api.extension.ConditionEvaluationResult import org.junit.jupiter.api.extension.ConditionEvaluationResult.disabled import org.junit.jupiter.api.extension.ConditionEvaluationResult.enabled @@ -14,8 +14,7 @@ import org.junit.platform.commons.util.Preconditions internal class EnabledIfBuildConfigValueCondition : ExecutionCondition { companion object { - private val ENABLED_BY_DEFAULT = - enabled("@EnabledIfBuildConfigValue is not present") + private val ENABLED_BY_DEFAULT = enabled("@EnabledIfBuildConfigValue is not present") } @RequiresApi(24) @@ -27,16 +26,25 @@ internal class EnabledIfBuildConfigValueCondition : ExecutionCondition { val name = annotation.named.trim() val regexString = annotation.matches - Preconditions.notBlank(name) { "The 'named' attribute must not be blank in $annotation" } - Preconditions.notBlank(regexString) { "The 'matches' attribute must not be blank in $annotation" } + Preconditions.notBlank(name) { + "The 'named' attribute must not be blank in $annotation" + } + Preconditions.notBlank(regexString) { + "The 'matches' attribute must not be blank in $annotation" + } - val actual = runCatching { BuildConfigValueUtils.getAsString(name) }.getOrNull() - ?: return disabled("BuildConfig key [$name] does not exist") + val actual = + runCatching { BuildConfigValueUtils.getAsString(name) }.getOrNull() + ?: return disabled("BuildConfig key [$name] does not exist") return if (actual.matches(regexString.toRegex())) { - enabled("BuildConfig key [$name] with value [$actual] matches regular expression [$regexString]") + enabled( + "BuildConfig key [$name] with value [$actual] matches regular expression [$regexString]" + ) } else { - disabled("BuildConfig key [$name] with value [$actual] does not match regular expression [$regexString]") + disabled( + "BuildConfig key [$name] with value [$actual] does not match regular expression [$regexString]" + ) } } diff --git a/instrumentation/core/src/main/java/de/mannodermaus/junit5/internal/EnabledOnManufacturerCondition.kt b/instrumentation/core/src/main/java/de/mannodermaus/junit5/internal/EnabledOnManufacturerCondition.kt index 5ab019bb..75e8bab0 100644 --- a/instrumentation/core/src/main/java/de/mannodermaus/junit5/internal/EnabledOnManufacturerCondition.kt +++ b/instrumentation/core/src/main/java/de/mannodermaus/junit5/internal/EnabledOnManufacturerCondition.kt @@ -1,7 +1,7 @@ package de.mannodermaus.junit5.internal -import androidx.annotation.RequiresApi import android.os.Build +import androidx.annotation.RequiresApi import de.mannodermaus.junit5.condition.EnabledOnManufacturer import org.junit.jupiter.api.extension.ConditionEvaluationResult import org.junit.jupiter.api.extension.ExecutionCondition @@ -16,11 +16,15 @@ internal class EnabledOnManufacturerCondition : ExecutionCondition { ConditionEvaluationResult.enabled("@EnabledOnManufacturer is not present") fun enabled(): ConditionEvaluationResult { - return ConditionEvaluationResult.enabled("Enabled on Manufacturer: " + Build.MANUFACTURER) + return ConditionEvaluationResult.enabled( + "Enabled on Manufacturer: " + Build.MANUFACTURER + ) } fun disabled(): ConditionEvaluationResult { - return ConditionEvaluationResult.disabled("Disabled on Manufacturer: " + Build.MANUFACTURER) + return ConditionEvaluationResult.disabled( + "Disabled on Manufacturer: " + Build.MANUFACTURER + ) } } @@ -35,7 +39,7 @@ internal class EnabledOnManufacturerCondition : ExecutionCondition { Preconditions.condition( patterns.isNotEmpty(), - "You must declare at least one Manufacturer in @EnabledOnManufacturer" + "You must declare at least one Manufacturer in @EnabledOnManufacturer", ) return if (patterns.any { Build.MANUFACTURER.equals(it, ignoreCase = ignoreCase) }) { diff --git a/instrumentation/core/src/main/java/de/mannodermaus/junit5/internal/EnabledOnSdkVersionCondition.kt b/instrumentation/core/src/main/java/de/mannodermaus/junit5/internal/EnabledOnSdkVersionCondition.kt index 6d0a6839..dc3823a2 100644 --- a/instrumentation/core/src/main/java/de/mannodermaus/junit5/internal/EnabledOnSdkVersionCondition.kt +++ b/instrumentation/core/src/main/java/de/mannodermaus/junit5/internal/EnabledOnSdkVersionCondition.kt @@ -1,7 +1,7 @@ package de.mannodermaus.junit5.internal -import androidx.annotation.RequiresApi import android.os.Build +import androidx.annotation.RequiresApi import de.mannodermaus.junit5.condition.EnabledOnSdkVersion import org.junit.jupiter.api.extension.ConditionEvaluationResult import org.junit.jupiter.api.extension.ExecutionCondition @@ -36,11 +36,12 @@ internal class EnabledOnSdkVersionCondition : ExecutionCondition { val hasUpperBound = untilApi != NOT_SET Preconditions.condition( hasLowerBound || hasUpperBound, - "At least one value must be provided in @EnabledOnSdkVersion" + "At least one value must be provided in @EnabledOnSdkVersion", ) // Constrain the current API Level based on the presence of "fromApi" & "untilApi": - // If either one is not set at all, that part of the conditional becomes true automatically + // If either one is not set at all, that part of the conditional becomes true + // automatically val lowerCheck = !hasLowerBound || Build.VERSION.SDK_INT >= fromApi val upperCheck = !hasUpperBound || Build.VERSION.SDK_INT <= untilApi return if (lowerCheck && upperCheck) { diff --git a/instrumentation/core/src/main/java/de/mannodermaus/junit5/internal/utils/BuildConfigValueUtils.kt b/instrumentation/core/src/main/java/de/mannodermaus/junit5/internal/utils/BuildConfigValueUtils.kt index 4a3a9fbc..e2f5e638 100644 --- a/instrumentation/core/src/main/java/de/mannodermaus/junit5/internal/utils/BuildConfigValueUtils.kt +++ b/instrumentation/core/src/main/java/de/mannodermaus/junit5/internal/utils/BuildConfigValueUtils.kt @@ -19,11 +19,12 @@ internal object BuildConfigValueUtils { fun getValue(key: String): String? { return try { - fieldCache.getOrPut(key) { - buildConfigClass.getField(key).also { - it.isAccessible = true + fieldCache + .getOrPut(key) { + buildConfigClass.getField(key).also { it.isAccessible = true } } - }.get(null)?.toString() + .get(null) + ?.toString() } catch (ignored: Throwable) { throw IllegalAccessException("Cannot access BuildConfig field '$key'") } @@ -40,8 +41,9 @@ internal object BuildConfigValueUtils { } /** - * Reflectively look up a BuildConfig field's value. - * This caches previous lookups to maximize performance. + * Reflectively look up a BuildConfig field's value. This caches previous lookups to maximize + * performance. + * * @param key Key of the entry to obtain * @return The value of this entry, if any */ @@ -54,4 +56,4 @@ internal object BuildConfigValueUtils { throw IllegalAccessException("Cannot access BuildConfig field'$key'") } } -} \ No newline at end of file +} diff --git a/instrumentation/core/src/six/kotlin/de/mannodermaus/junit5/CoreConstants.kt b/instrumentation/core/src/six/kotlin/de/mannodermaus/junit5/CoreConstants.kt index bf6f6e8c..27dd1ed0 100644 --- a/instrumentation/core/src/six/kotlin/de/mannodermaus/junit5/CoreConstants.kt +++ b/instrumentation/core/src/six/kotlin/de/mannodermaus/junit5/CoreConstants.kt @@ -1,7 +1,7 @@ package de.mannodermaus.junit5 /** - * The minimum Android API level on which JUnit Framework tests may be executed. - * Trying to launch a test on an older device will simply mark it as 'skipped'. + * The minimum Android API level on which JUnit Framework tests may be executed. Trying to launch a + * test on an older device will simply mark it as 'skipped'. */ public const val JUNIT_FRAMEWORK_MINIMUM_SDK_VERSION: Int = 35 diff --git a/instrumentation/core/src/six/kotlin/de/mannodermaus/junit5/internal/compat/ExtensionContextCompat.kt b/instrumentation/core/src/six/kotlin/de/mannodermaus/junit5/internal/compat/ExtensionContextCompat.kt index 2c1c9983..af5144ad 100644 --- a/instrumentation/core/src/six/kotlin/de/mannodermaus/junit5/internal/compat/ExtensionContextCompat.kt +++ b/instrumentation/core/src/six/kotlin/de/mannodermaus/junit5/internal/compat/ExtensionContextCompat.kt @@ -1,18 +1,18 @@ package de.mannodermaus.junit5.internal.compat -import org.junit.jupiter.api.extension.ExtensionContext import kotlin.reflect.KClass +import org.junit.jupiter.api.extension.ExtensionContext // JUnit 6 facade of ExtensionContext.Store APIs // that didn't exist in previous versions of the framework. internal fun ExtensionContext.Store.computeIfAbsentCompat( key: K, - defaultCreator: (K) -> V + defaultCreator: (K) -> V, ): Any = computeIfAbsent(key, defaultCreator) internal fun ExtensionContext.Store.computeIfAbsentCompat( key: K, defaultCreator: (K) -> V, - requiredType: KClass + requiredType: KClass, ): V = computeIfAbsent(key, defaultCreator, requiredType.java) diff --git a/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/AbstractExecutionConditionTests.kt b/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/AbstractExecutionConditionTests.kt index cabe47ee..e59cea27 100644 --- a/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/AbstractExecutionConditionTests.kt +++ b/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/AbstractExecutionConditionTests.kt @@ -1,6 +1,8 @@ package de.mannodermaus.junit5.condition import com.google.common.truth.Truth.assertThat +import java.lang.reflect.AnnotatedElement +import java.util.* import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.TestInfo @@ -10,50 +12,47 @@ import org.junit.jupiter.api.extension.ExtensionContext import org.junit.platform.commons.util.ReflectionUtils import org.mockito.kotlin.mock import org.mockito.kotlin.whenever -import java.lang.reflect.AnnotatedElement -import java.util.* abstract class AbstractExecutionConditionTests { - private val context = mock() - private var result: ConditionEvaluationResult? = null + private val context = mock() + private var result: ConditionEvaluationResult? = null - /* Lifecycle */ + /* Lifecycle */ - @BeforeEach - fun beforeEach(testInfo: TestInfo) { - whenever(context.element).thenReturn(method(testInfo)) - } + @BeforeEach + fun beforeEach(testInfo: TestInfo) { + whenever(context.element).thenReturn(method(testInfo)) + } - /* Abstract */ + /* Abstract */ - abstract fun getTestClass(): Class<*> + abstract fun getTestClass(): Class<*> - abstract fun getExecutionCondition(): ExecutionCondition + abstract fun getExecutionCondition(): ExecutionCondition - /* Protected */ + /* Protected */ - protected fun evaluateCondition() { - this.result = getExecutionCondition().evaluateExecutionCondition(context) - } + protected fun evaluateCondition() { + this.result = getExecutionCondition().evaluateExecutionCondition(context) + } - protected fun assertEnabled() { - assertTrue(!result!!.isDisabled, "Should be enabled") - } + protected fun assertEnabled() { + assertTrue(!result!!.isDisabled, "Should be enabled") + } - protected fun assertDisabled() { - assertTrue(result!!.isDisabled, "Should be disabled") - } + protected fun assertDisabled() { + assertTrue(result!!.isDisabled, "Should be disabled") + } - protected fun assertReasonEquals(text: String) { - assertThat(result!!.reason).hasValue(text) - } + protected fun assertReasonEquals(text: String) { + assertThat(result!!.reason).hasValue(text) + } - /* Private */ + /* Private */ - private fun method(testInfo: TestInfo) = - method(getTestClass(), testInfo.testMethod.get().name) + private fun method(testInfo: TestInfo) = method(getTestClass(), testInfo.testMethod.get().name) - private fun method(clazz: Class<*>, methodName: String): Optional = - Optional.of(ReflectionUtils.findMethod(clazz, methodName).get()) + private fun method(clazz: Class<*>, methodName: String): Optional = + Optional.of(ReflectionUtils.findMethod(clazz, methodName).get()) } diff --git a/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/DisabledIfBuildConfigValueConditionTests.kt b/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/DisabledIfBuildConfigValueConditionTests.kt index 9b1bf077..73d7c006 100644 --- a/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/DisabledIfBuildConfigValueConditionTests.kt +++ b/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/DisabledIfBuildConfigValueConditionTests.kt @@ -13,78 +13,71 @@ import org.junit.platform.commons.PreconditionViolationException /** * Unit tests for [DisabledIfBuildConfigValueCondition]. * - * This works together with [DisabledIfBuildConfigValueIntegrationTests]: The test methods - * in both classes MUST be named identical. + * This works together with [DisabledIfBuildConfigValueIntegrationTests]: The test methods in both + * classes MUST be named identical. */ @ResourceLock(RESOURCE_LOCK_INSTRUMENTATION) class DisabledIfBuildConfigValueConditionTests : AbstractExecutionConditionTests() { - override fun getExecutionCondition(): ExecutionCondition = - DisabledIfBuildConfigValueCondition() + override fun getExecutionCondition(): ExecutionCondition = DisabledIfBuildConfigValueCondition() - override fun getTestClass(): Class<*> = DisabledIfBuildConfigValueIntegrationTests::class.java + override fun getTestClass(): Class<*> = DisabledIfBuildConfigValueIntegrationTests::class.java - /** - * @see [DisabledIfBuildConfigValueIntegrationTests.invalidBecauseNameIsEmpty] - */ - @Test - fun invalidBecauseNameIsEmpty() { - withMockedInstrumentation { - val expected = assertThrows { - evaluateCondition() - } + /** @see [DisabledIfBuildConfigValueIntegrationTests.invalidBecauseNameIsEmpty] */ + @Test + fun invalidBecauseNameIsEmpty() { + withMockedInstrumentation { + val expected = assertThrows { evaluateCondition() } - assertThat(expected).hasMessageThat().contains("The 'named' attribute must not be blank in") + assertThat(expected) + .hasMessageThat() + .contains("The 'named' attribute must not be blank in") + } } - } - /** - * @see [DisabledIfBuildConfigValueIntegrationTests.invalidBecauseRegexIsEmpty] - */ - @Test - fun invalidBecauseRegexIsEmpty() { - withMockedInstrumentation { - val expected = assertThrows { - evaluateCondition() - } + /** @see [DisabledIfBuildConfigValueIntegrationTests.invalidBecauseRegexIsEmpty] */ + @Test + fun invalidBecauseRegexIsEmpty() { + withMockedInstrumentation { + val expected = assertThrows { evaluateCondition() } - assertThat(expected).hasMessageThat().contains("The 'matches' attribute must not be blank in") + assertThat(expected) + .hasMessageThat() + .contains("The 'matches' attribute must not be blank in") + } } - } - /** - * @see [DisabledIfBuildConfigValueIntegrationTests.disabledBecauseValueMatchesRegex] - */ - @Test - fun disabledBecauseValueMatchesRegex() { - withMockedInstrumentation { - evaluateCondition() - assertDisabled() - assertReasonEquals("BuildConfig key [DEBUG] with value [true] matches regular expression [\\w{4}]") + /** @see [DisabledIfBuildConfigValueIntegrationTests.disabledBecauseValueMatchesRegex] */ + @Test + fun disabledBecauseValueMatchesRegex() { + withMockedInstrumentation { + evaluateCondition() + assertDisabled() + assertReasonEquals( + "BuildConfig key [DEBUG] with value [true] matches regular expression [\\w{4}]" + ) + } } - } - /** - * @see [DisabledIfBuildConfigValueIntegrationTests.enabledBecauseValueDoesNotMatchRegex] - */ - @Test - fun enabledBecauseValueDoesNotMatchRegex() { - withMockedInstrumentation { - evaluateCondition() - assertEnabled() - assertReasonEquals("BuildConfig key [VERSION_NAME] with value [1.0] does not match regular expression [0.1.234]") + /** @see [DisabledIfBuildConfigValueIntegrationTests.enabledBecauseValueDoesNotMatchRegex] */ + @Test + fun enabledBecauseValueDoesNotMatchRegex() { + withMockedInstrumentation { + evaluateCondition() + assertEnabled() + assertReasonEquals( + "BuildConfig key [VERSION_NAME] with value [1.0] does not match regular expression [0.1.234]" + ) + } } - } - /** - * @see [DisabledIfBuildConfigValueIntegrationTests.enabledBecauseKeyDoesNotExist] - */ - @Test - fun enabledBecauseKeyDoesNotExist() { - withMockedInstrumentation { - evaluateCondition() - assertEnabled() - assertReasonEquals("BuildConfig key [NOT_EXISTENT_KEY] does not exist") + /** @see [DisabledIfBuildConfigValueIntegrationTests.enabledBecauseKeyDoesNotExist] */ + @Test + fun enabledBecauseKeyDoesNotExist() { + withMockedInstrumentation { + evaluateCondition() + assertEnabled() + assertReasonEquals("BuildConfig key [NOT_EXISTENT_KEY] does not exist") + } } - } } diff --git a/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/DisabledIfBuildConfigValueIntegrationTests.kt b/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/DisabledIfBuildConfigValueIntegrationTests.kt index 2d2807f1..988afcdc 100644 --- a/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/DisabledIfBuildConfigValueIntegrationTests.kt +++ b/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/DisabledIfBuildConfigValueIntegrationTests.kt @@ -4,39 +4,34 @@ import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test /** - * Companion class for [DisabledIfBuildConfigValueConditionTests]. - * The tests in here are intentionally disabled; the partner class will - * drive them through reflection in order to assert the behavior of the condition + * Companion class for [DisabledIfBuildConfigValueConditionTests]. The tests in here are + * intentionally disabled; the partner class will drive them through reflection in order to assert + * the behavior of the condition */ class DisabledIfBuildConfigValueIntegrationTests { - @Disabled("Used by DisabledIfBuildConfigValueConditionTests only") - @DisabledIfBuildConfigValue(named = "", matches = ".*") - @Test - fun invalidBecauseNameIsEmpty() { - } + @Disabled("Used by DisabledIfBuildConfigValueConditionTests only") + @DisabledIfBuildConfigValue(named = "", matches = ".*") + @Test + fun invalidBecauseNameIsEmpty() {} - @Disabled("Used by DisabledIfBuildConfigValueConditionTests only") - @DisabledIfBuildConfigValue(named = "DEBUG", matches = "") - @Test - fun invalidBecauseRegexIsEmpty() { - } + @Disabled("Used by DisabledIfBuildConfigValueConditionTests only") + @DisabledIfBuildConfigValue(named = "DEBUG", matches = "") + @Test + fun invalidBecauseRegexIsEmpty() {} - @Disabled("Used by DisabledIfBuildConfigValueConditionTests only") - @DisabledIfBuildConfigValue(named = "DEBUG", matches = "\\w{4}") - @Test - fun disabledBecauseValueMatchesRegex() { - } + @Disabled("Used by DisabledIfBuildConfigValueConditionTests only") + @DisabledIfBuildConfigValue(named = "DEBUG", matches = "\\w{4}") + @Test + fun disabledBecauseValueMatchesRegex() {} - @Disabled("Used by DisabledIfBuildConfigValueConditionTests only") - @DisabledIfBuildConfigValue(named = "VERSION_NAME", matches = "0.1.234") - @Test - fun enabledBecauseValueDoesNotMatchRegex() { - } + @Disabled("Used by DisabledIfBuildConfigValueConditionTests only") + @DisabledIfBuildConfigValue(named = "VERSION_NAME", matches = "0.1.234") + @Test + fun enabledBecauseValueDoesNotMatchRegex() {} - @Disabled("Used by DisabledIfBuildConfigValueConditionTests only") - @DisabledIfBuildConfigValue(named = "NOT_EXISTENT_KEY", matches = "whatever") - @Test - fun enabledBecauseKeyDoesNotExist() { - } + @Disabled("Used by DisabledIfBuildConfigValueConditionTests only") + @DisabledIfBuildConfigValue(named = "NOT_EXISTENT_KEY", matches = "whatever") + @Test + fun enabledBecauseKeyDoesNotExist() {} } diff --git a/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/DisabledOnManufacturerConditionTests.kt b/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/DisabledOnManufacturerConditionTests.kt index 20b1eed5..09f4c3d5 100644 --- a/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/DisabledOnManufacturerConditionTests.kt +++ b/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/DisabledOnManufacturerConditionTests.kt @@ -11,85 +11,74 @@ import org.junit.platform.commons.PreconditionViolationException /** * Unit tests for [DisabledOnManufacturerCondition]. * - * This works together with [DisabledOnManufacturerIntegrationTests]: The test methods - * in both classes MUST be named identical. + * This works together with [DisabledOnManufacturerIntegrationTests]: The test methods in both + * classes MUST be named identical. */ class DisabledOnManufacturerConditionTests : AbstractExecutionConditionTests() { - override fun getExecutionCondition(): ExecutionCondition = - DisabledOnManufacturerCondition() + override fun getExecutionCondition(): ExecutionCondition = DisabledOnManufacturerCondition() - override fun getTestClass(): Class<*> = DisabledOnManufacturerIntegrationTests::class.java + override fun getTestClass(): Class<*> = DisabledOnManufacturerIntegrationTests::class.java - /** - * @see [DisabledOnManufacturerIntegrationTests.invalidBecauseArrayIsEmpty] - */ - @Test - fun invalidBecauseArrayIsEmpty() { - val expected = assertThrows { - evaluateCondition() - } + /** @see [DisabledOnManufacturerIntegrationTests.invalidBecauseArrayIsEmpty] */ + @Test + fun invalidBecauseArrayIsEmpty() { + val expected = assertThrows { evaluateCondition() } - assertThat(expected).hasMessageThat().contains("You must declare at least one Manufacturer in @DisabledOnManufacturer") - } + assertThat(expected) + .hasMessageThat() + .contains("You must declare at least one Manufacturer in @DisabledOnManufacturer") + } - /** - * @see [DisabledOnManufacturerIntegrationTests.disabledBecauseValueMatchesExactly] - */ - @Test - fun disabledBecauseValueMatchesExactly() { - withManufacturer("Samsung") { - evaluateCondition() - assertDisabled() - assertReasonEquals("Disabled on Manufacturer: Samsung") + /** @see [DisabledOnManufacturerIntegrationTests.disabledBecauseValueMatchesExactly] */ + @Test + fun disabledBecauseValueMatchesExactly() { + withManufacturer("Samsung") { + evaluateCondition() + assertDisabled() + assertReasonEquals("Disabled on Manufacturer: Samsung") + } } - } - /** - * @see [DisabledOnManufacturerIntegrationTests.disabledBecauseValueIsAmongTheValues] - */ - @Test - fun disabledBecauseValueIsAmongTheValues() { - withManufacturer("Huawei") { - evaluateCondition() - assertDisabled() - assertReasonEquals("Disabled on Manufacturer: Huawei") + /** @see [DisabledOnManufacturerIntegrationTests.disabledBecauseValueIsAmongTheValues] */ + @Test + fun disabledBecauseValueIsAmongTheValues() { + withManufacturer("Huawei") { + evaluateCondition() + assertDisabled() + assertReasonEquals("Disabled on Manufacturer: Huawei") + } } - } - /** - * @see [DisabledOnManufacturerIntegrationTests.disabledBecauseValueMatchesWithOfIgnoreCase] - */ - @Test - fun disabledBecauseValueMatchesWithOfIgnoreCase() { - withManufacturer("Samsung") { - evaluateCondition() - assertDisabled() - assertReasonEquals("Disabled on Manufacturer: Samsung") + /** @see [DisabledOnManufacturerIntegrationTests.disabledBecauseValueMatchesWithOfIgnoreCase] */ + @Test + fun disabledBecauseValueMatchesWithOfIgnoreCase() { + withManufacturer("Samsung") { + evaluateCondition() + assertDisabled() + assertReasonEquals("Disabled on Manufacturer: Samsung") + } } - } - /** - * @see [DisabledOnManufacturerIntegrationTests.enabledBecauseValueDoesntMatchDueToIgnoreCase] - */ - @Test - fun enabledBecauseValueDoesntMatchDueToIgnoreCase() { - withManufacturer("Samsung") { - evaluateCondition() - assertEnabled() - assertReasonEquals("Enabled on Manufacturer: Samsung") + /** + * @see [DisabledOnManufacturerIntegrationTests.enabledBecauseValueDoesntMatchDueToIgnoreCase] + */ + @Test + fun enabledBecauseValueDoesntMatchDueToIgnoreCase() { + withManufacturer("Samsung") { + evaluateCondition() + assertEnabled() + assertReasonEquals("Enabled on Manufacturer: Samsung") + } } - } - /** - * @see [DisabledOnManufacturerIntegrationTests.enabledBecauseValueDoesntMatchAnyValue] - */ - @Test - fun enabledBecauseValueDoesntMatchAnyValue() { - withManufacturer("Google") { - evaluateCondition() - assertEnabled() - assertReasonEquals("Enabled on Manufacturer: Google") + /** @see [DisabledOnManufacturerIntegrationTests.enabledBecauseValueDoesntMatchAnyValue] */ + @Test + fun enabledBecauseValueDoesntMatchAnyValue() { + withManufacturer("Google") { + evaluateCondition() + assertEnabled() + assertReasonEquals("Enabled on Manufacturer: Google") + } } - } } diff --git a/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/DisabledOnManufacturerIntegrationTests.kt b/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/DisabledOnManufacturerIntegrationTests.kt index 45dd1cd1..65aab7af 100644 --- a/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/DisabledOnManufacturerIntegrationTests.kt +++ b/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/DisabledOnManufacturerIntegrationTests.kt @@ -4,45 +4,39 @@ import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test /** - * Companion class for [DisabledOnManufacturerConditionTests]. - * The tests in here are intentionally disabled; the partner class will - * drive them through reflection in order to assert the behavior of the condition + * Companion class for [DisabledOnManufacturerConditionTests]. The tests in here are intentionally + * disabled; the partner class will drive them through reflection in order to assert the behavior of + * the condition */ class DisabledOnManufacturerIntegrationTests { - @Disabled("Used by DisabledOnManufacturerConditionTests only") - @DisabledOnManufacturer([]) - @Test - fun invalidBecauseArrayIsEmpty() { - } - - @Disabled("Used by DisabledOnManufacturerConditionTests only") - @DisabledOnManufacturer(["Samsung"]) - @Test - fun disabledBecauseValueMatchesExactly() { - } - - @Disabled("Used by DisabledOnManufacturerConditionTests only") - @DisabledOnManufacturer(["Samsung", "Huawei"]) - @Test - fun disabledBecauseValueIsAmongTheValues() { - } - - @Disabled("Used by DisabledOnManufacturerConditionTests only") - @DisabledOnManufacturer(["sAmSuNg"]) - @Test - fun disabledBecauseValueMatchesWithOfIgnoreCase() { - } - - @Disabled("Used by DisabledOnManufacturerConditionTests only") - @DisabledOnManufacturer(["sAmSuNg"], ignoreCase = false) - @Test - fun enabledBecauseValueDoesntMatchDueToIgnoreCase() { - } - - @Disabled("Used by DisabledOnManufacturerConditionTests only") - @DisabledOnManufacturer(["Samsung", "Huawei"]) - @Test - fun enabledBecauseValueDoesntMatchAnyValue() { - } + @Disabled("Used by DisabledOnManufacturerConditionTests only") + @DisabledOnManufacturer([]) + @Test + fun invalidBecauseArrayIsEmpty() {} + + @Disabled("Used by DisabledOnManufacturerConditionTests only") + @DisabledOnManufacturer(["Samsung"]) + @Test + fun disabledBecauseValueMatchesExactly() {} + + @Disabled("Used by DisabledOnManufacturerConditionTests only") + @DisabledOnManufacturer(["Samsung", "Huawei"]) + @Test + fun disabledBecauseValueIsAmongTheValues() {} + + @Disabled("Used by DisabledOnManufacturerConditionTests only") + @DisabledOnManufacturer(["sAmSuNg"]) + @Test + fun disabledBecauseValueMatchesWithOfIgnoreCase() {} + + @Disabled("Used by DisabledOnManufacturerConditionTests only") + @DisabledOnManufacturer(["sAmSuNg"], ignoreCase = false) + @Test + fun enabledBecauseValueDoesntMatchDueToIgnoreCase() {} + + @Disabled("Used by DisabledOnManufacturerConditionTests only") + @DisabledOnManufacturer(["Samsung", "Huawei"]) + @Test + fun enabledBecauseValueDoesntMatchAnyValue() {} } diff --git a/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/DisabledOnSdkVersionConditionTests.kt b/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/DisabledOnSdkVersionConditionTests.kt index 3daa5bf6..b9a7c775 100644 --- a/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/DisabledOnSdkVersionConditionTests.kt +++ b/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/DisabledOnSdkVersionConditionTests.kt @@ -11,97 +11,82 @@ import org.junit.platform.commons.PreconditionViolationException /** * Unit tests for [DisabledOnSdkVersionCondition]. * - * This works together with [DisabledOnSdkVersionIntegrationTests]: The test methods - * in both classes MUST be named identical. + * This works together with [DisabledOnSdkVersionIntegrationTests]: The test methods in both classes + * MUST be named identical. */ class DisabledOnSdkVersionConditionTests : AbstractExecutionConditionTests() { - override fun getExecutionCondition(): ExecutionCondition = - DisabledOnSdkVersionCondition() + override fun getExecutionCondition(): ExecutionCondition = DisabledOnSdkVersionCondition() - override fun getTestClass(): Class<*> = DisabledOnSdkVersionIntegrationTests::class.java + override fun getTestClass(): Class<*> = DisabledOnSdkVersionIntegrationTests::class.java - /** - * @see [DisabledOnSdkVersionIntegrationTests.invalidBecauseNoValueGiven] - */ - @Test - fun invalidBecauseNoValueGiven() { - val expected = assertThrows { - evaluateCondition() - } + /** @see [DisabledOnSdkVersionIntegrationTests.invalidBecauseNoValueGiven] */ + @Test + fun invalidBecauseNoValueGiven() { + val expected = assertThrows { evaluateCondition() } - assertThat(expected).hasMessageThat().contains("At least one value must be provided in @DisabledOnSdkVersion") - } + assertThat(expected) + .hasMessageThat() + .contains("At least one value must be provided in @DisabledOnSdkVersion") + } - /** - * @see [DisabledOnSdkVersionIntegrationTests.disabledBecauseMinApiIsMatched] - */ - @Test - fun disabledBecauseMinApiIsMatched() { - withApiLevel(26) { - evaluateCondition() - assertDisabled() - assertReasonEquals("Disabled on API 26") + /** @see [DisabledOnSdkVersionIntegrationTests.disabledBecauseMinApiIsMatched] */ + @Test + fun disabledBecauseMinApiIsMatched() { + withApiLevel(26) { + evaluateCondition() + assertDisabled() + assertReasonEquals("Disabled on API 26") + } } - } - /** - * @see [DisabledOnSdkVersionIntegrationTests.disabledBecauseMaxApiIsMatched] - */ - @Test - fun disabledBecauseMaxApiIsMatched() { - withApiLevel(24) { - evaluateCondition() - assertDisabled() - assertReasonEquals("Disabled on API 24") + /** @see [DisabledOnSdkVersionIntegrationTests.disabledBecauseMaxApiIsMatched] */ + @Test + fun disabledBecauseMaxApiIsMatched() { + withApiLevel(24) { + evaluateCondition() + assertDisabled() + assertReasonEquals("Disabled on API 24") + } } - } - /** - * @see [DisabledOnSdkVersionIntegrationTests.disabledBecauseApiIsInValidRange] - */ - @Test - fun disabledBecauseApiIsInValidRange() { - withApiLevel(26) { - evaluateCondition() - assertDisabled() - assertReasonEquals("Disabled on API 26") + /** @see [DisabledOnSdkVersionIntegrationTests.disabledBecauseApiIsInValidRange] */ + @Test + fun disabledBecauseApiIsInValidRange() { + withApiLevel(26) { + evaluateCondition() + assertDisabled() + assertReasonEquals("Disabled on API 26") + } } - } - /** - * @see [DisabledOnSdkVersionIntegrationTests.enabledBecauseMinApiLowEnough] - */ - @Test - fun enabledBecauseMinApiLowEnough() { - withApiLevel(26) { - evaluateCondition() - assertEnabled() - assertReasonEquals("Enabled on API 26") + /** @see [DisabledOnSdkVersionIntegrationTests.enabledBecauseMinApiLowEnough] */ + @Test + fun enabledBecauseMinApiLowEnough() { + withApiLevel(26) { + evaluateCondition() + assertEnabled() + assertReasonEquals("Enabled on API 26") + } } - } - /** - * @see [DisabledOnSdkVersionIntegrationTests.disabledBecauseMaxApiHighEnough] - */ - @Test - fun disabledBecauseMaxApiHighEnough() { - withApiLevel(29) { - evaluateCondition() - assertEnabled() - assertReasonEquals("Enabled on API 29") + /** @see [DisabledOnSdkVersionIntegrationTests.disabledBecauseMaxApiHighEnough] */ + @Test + fun disabledBecauseMaxApiHighEnough() { + withApiLevel(29) { + evaluateCondition() + assertEnabled() + assertReasonEquals("Enabled on API 29") + } } - } - /** - * @see [DisabledOnSdkVersionIntegrationTests.disabledBecauseApiIsInsideValidRange] - */ - @Test - fun disabledBecauseApiIsInsideValidRange() { - withApiLevel(28) { - evaluateCondition() - assertDisabled() - assertReasonEquals("Disabled on API 28") + /** @see [DisabledOnSdkVersionIntegrationTests.disabledBecauseApiIsInsideValidRange] */ + @Test + fun disabledBecauseApiIsInsideValidRange() { + withApiLevel(28) { + evaluateCondition() + assertDisabled() + assertReasonEquals("Disabled on API 28") + } } - } } diff --git a/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/DisabledOnSdkVersionIntegrationTests.kt b/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/DisabledOnSdkVersionIntegrationTests.kt index 6cef4f62..9c9a76e1 100644 --- a/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/DisabledOnSdkVersionIntegrationTests.kt +++ b/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/DisabledOnSdkVersionIntegrationTests.kt @@ -4,51 +4,44 @@ import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test /** - * Companion class for [DisabledOnSdkVersionConditionTests]. - * The tests in here are intentionally disabled; the partner class will - * drive them through reflection in order to assert the behavior of the condition + * Companion class for [DisabledOnSdkVersionConditionTests]. The tests in here are intentionally + * disabled; the partner class will drive them through reflection in order to assert the behavior of + * the condition */ class DisabledOnSdkVersionIntegrationTests { - @Disabled("Used by DisabledOnSdkVersionConditionTests only") - @DisabledOnSdkVersion - @Test - fun invalidBecauseNoValueGiven() { - } - - @Disabled("Used by DisabledOnSdkVersionConditionTests only") - @DisabledOnSdkVersion(from = 24) - @Test - fun disabledBecauseMinApiIsMatched() { - } - - @Disabled("Used by DisabledOnSdkVersionConditionTests only") - @DisabledOnSdkVersion(until = 26) - @Test - fun disabledBecauseMaxApiIsMatched() { - } - - @Disabled("Used by DisabledOnSdkVersionConditionTests only") - @DisabledOnSdkVersion(from = 24, until = 29) - @Test - fun disabledBecauseApiIsInValidRange() { - } - - @Disabled("Used by DisabledOnSdkVersionConditionTests only") - @DisabledOnSdkVersion(from = 27) - @Test - fun enabledBecauseMinApiLowEnough() { - } - - @Disabled("Used by DisabledOnSdkVersionConditionTests only") - @DisabledOnSdkVersion(until = 27) - @Test - fun disabledBecauseMaxApiHighEnough() { - } - - @Disabled("Used by DisabledOnSdkVersionConditionTests only") - @DisabledOnSdkVersion(from = 27, until = 29) - @Test - fun disabledBecauseApiIsInsideValidRange() { - } + @Disabled("Used by DisabledOnSdkVersionConditionTests only") + @DisabledOnSdkVersion + @Test + fun invalidBecauseNoValueGiven() {} + + @Disabled("Used by DisabledOnSdkVersionConditionTests only") + @DisabledOnSdkVersion(from = 24) + @Test + fun disabledBecauseMinApiIsMatched() {} + + @Disabled("Used by DisabledOnSdkVersionConditionTests only") + @DisabledOnSdkVersion(until = 26) + @Test + fun disabledBecauseMaxApiIsMatched() {} + + @Disabled("Used by DisabledOnSdkVersionConditionTests only") + @DisabledOnSdkVersion(from = 24, until = 29) + @Test + fun disabledBecauseApiIsInValidRange() {} + + @Disabled("Used by DisabledOnSdkVersionConditionTests only") + @DisabledOnSdkVersion(from = 27) + @Test + fun enabledBecauseMinApiLowEnough() {} + + @Disabled("Used by DisabledOnSdkVersionConditionTests only") + @DisabledOnSdkVersion(until = 27) + @Test + fun disabledBecauseMaxApiHighEnough() {} + + @Disabled("Used by DisabledOnSdkVersionConditionTests only") + @DisabledOnSdkVersion(from = 27, until = 29) + @Test + fun disabledBecauseApiIsInsideValidRange() {} } diff --git a/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/EnabledIfBuildConfigValueConditionTests.kt b/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/EnabledIfBuildConfigValueConditionTests.kt index a772928d..85538f48 100644 --- a/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/EnabledIfBuildConfigValueConditionTests.kt +++ b/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/EnabledIfBuildConfigValueConditionTests.kt @@ -13,78 +13,71 @@ import org.junit.platform.commons.PreconditionViolationException /** * Unit tests for [EnabledIfBuildConfigValueCondition]. * - * This works together with [EnabledIfBuildConfigValueIntegrationTests]: The test methods - * in both classes MUST be named identical. + * This works together with [EnabledIfBuildConfigValueIntegrationTests]: The test methods in both + * classes MUST be named identical. */ @ResourceLock(RESOURCE_LOCK_INSTRUMENTATION) class EnabledIfBuildConfigValueConditionTests : AbstractExecutionConditionTests() { - override fun getExecutionCondition(): ExecutionCondition = - EnabledIfBuildConfigValueCondition() + override fun getExecutionCondition(): ExecutionCondition = EnabledIfBuildConfigValueCondition() - override fun getTestClass(): Class<*> = EnabledIfBuildConfigValueIntegrationTests::class.java + override fun getTestClass(): Class<*> = EnabledIfBuildConfigValueIntegrationTests::class.java - /** - * @see [EnabledIfBuildConfigValueIntegrationTests.invalidBecauseNameIsEmpty] - */ - @Test - fun invalidBecauseNameIsEmpty() { - withMockedInstrumentation { - val expected = assertThrows { - evaluateCondition() - } + /** @see [EnabledIfBuildConfigValueIntegrationTests.invalidBecauseNameIsEmpty] */ + @Test + fun invalidBecauseNameIsEmpty() { + withMockedInstrumentation { + val expected = assertThrows { evaluateCondition() } - assertThat(expected).hasMessageThat().contains("The 'named' attribute must not be blank in") + assertThat(expected) + .hasMessageThat() + .contains("The 'named' attribute must not be blank in") + } } - } - /** - * @see [EnabledIfBuildConfigValueIntegrationTests.invalidBecauseRegexIsEmpty] - */ - @Test - fun invalidBecauseRegexIsEmpty() { - withMockedInstrumentation { - val expected = assertThrows { - evaluateCondition() - } + /** @see [EnabledIfBuildConfigValueIntegrationTests.invalidBecauseRegexIsEmpty] */ + @Test + fun invalidBecauseRegexIsEmpty() { + withMockedInstrumentation { + val expected = assertThrows { evaluateCondition() } - assertThat(expected).hasMessageThat().contains("The 'matches' attribute must not be blank in") + assertThat(expected) + .hasMessageThat() + .contains("The 'matches' attribute must not be blank in") + } } - } - /** - * @see [EnabledIfBuildConfigValueIntegrationTests.enabledBecauseValueMatchesRegex] - */ - @Test - fun enabledBecauseValueMatchesRegex() { - withMockedInstrumentation { - evaluateCondition() - assertEnabled() - assertReasonEquals("BuildConfig key [DEBUG] with value [true] matches regular expression [\\w{4}]") + /** @see [EnabledIfBuildConfigValueIntegrationTests.enabledBecauseValueMatchesRegex] */ + @Test + fun enabledBecauseValueMatchesRegex() { + withMockedInstrumentation { + evaluateCondition() + assertEnabled() + assertReasonEquals( + "BuildConfig key [DEBUG] with value [true] matches regular expression [\\w{4}]" + ) + } } - } - /** - * @see [EnabledIfBuildConfigValueIntegrationTests.disabledBecauseValueDoesNotMatchRegex] - */ - @Test - fun disabledBecauseValueDoesNotMatchRegex() { - withMockedInstrumentation { - evaluateCondition() - assertDisabled() - assertReasonEquals("BuildConfig key [VERSION_NAME] with value [1.0] does not match regular expression [0.1.234]") + /** @see [EnabledIfBuildConfigValueIntegrationTests.disabledBecauseValueDoesNotMatchRegex] */ + @Test + fun disabledBecauseValueDoesNotMatchRegex() { + withMockedInstrumentation { + evaluateCondition() + assertDisabled() + assertReasonEquals( + "BuildConfig key [VERSION_NAME] with value [1.0] does not match regular expression [0.1.234]" + ) + } } - } - /** - * @see [EnabledIfBuildConfigValueIntegrationTests.disabledBecauseKeyDoesNotExist] - */ - @Test - fun disabledBecauseKeyDoesNotExist() { - withMockedInstrumentation { - evaluateCondition() - assertDisabled() - assertReasonEquals("BuildConfig key [NOT_EXISTENT_KEY] does not exist") + /** @see [EnabledIfBuildConfigValueIntegrationTests.disabledBecauseKeyDoesNotExist] */ + @Test + fun disabledBecauseKeyDoesNotExist() { + withMockedInstrumentation { + evaluateCondition() + assertDisabled() + assertReasonEquals("BuildConfig key [NOT_EXISTENT_KEY] does not exist") + } } - } } diff --git a/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/EnabledIfBuildConfigValueIntegrationTests.kt b/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/EnabledIfBuildConfigValueIntegrationTests.kt index fc7a798e..324c42ee 100644 --- a/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/EnabledIfBuildConfigValueIntegrationTests.kt +++ b/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/EnabledIfBuildConfigValueIntegrationTests.kt @@ -4,39 +4,34 @@ import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test /** - * Companion class for [EnabledIfBuildConfigValueConditionTests]. - * The tests in here are intentionally disabled; the partner class will - * drive them through reflection in order to assert the behavior of the condition + * Companion class for [EnabledIfBuildConfigValueConditionTests]. The tests in here are + * intentionally disabled; the partner class will drive them through reflection in order to assert + * the behavior of the condition */ class EnabledIfBuildConfigValueIntegrationTests { - @Disabled("Used by EnabledIfBuildConfigValueConditionTests only") - @EnabledIfBuildConfigValue(named = "", matches = ".*") - @Test - fun invalidBecauseNameIsEmpty() { - } + @Disabled("Used by EnabledIfBuildConfigValueConditionTests only") + @EnabledIfBuildConfigValue(named = "", matches = ".*") + @Test + fun invalidBecauseNameIsEmpty() {} - @Disabled("Used by EnabledIfBuildConfigValueConditionTests only") - @EnabledIfBuildConfigValue(named = "DEBUG", matches = "") - @Test - fun invalidBecauseRegexIsEmpty() { - } + @Disabled("Used by EnabledIfBuildConfigValueConditionTests only") + @EnabledIfBuildConfigValue(named = "DEBUG", matches = "") + @Test + fun invalidBecauseRegexIsEmpty() {} - @Disabled("Used by EnabledIfBuildConfigValueConditionTests only") - @EnabledIfBuildConfigValue(named = "DEBUG", matches = "\\w{4}") - @Test - fun enabledBecauseValueMatchesRegex() { - } + @Disabled("Used by EnabledIfBuildConfigValueConditionTests only") + @EnabledIfBuildConfigValue(named = "DEBUG", matches = "\\w{4}") + @Test + fun enabledBecauseValueMatchesRegex() {} - @Disabled("Used by EnabledIfBuildConfigValueConditionTests only") - @EnabledIfBuildConfigValue(named = "VERSION_NAME", matches = "0.1.234") - @Test - fun disabledBecauseValueDoesNotMatchRegex() { - } + @Disabled("Used by EnabledIfBuildConfigValueConditionTests only") + @EnabledIfBuildConfigValue(named = "VERSION_NAME", matches = "0.1.234") + @Test + fun disabledBecauseValueDoesNotMatchRegex() {} - @Disabled("Used by EnabledIfBuildConfigValueConditionTests only") - @EnabledIfBuildConfigValue(named = "NOT_EXISTENT_KEY", matches = "whatever") - @Test - fun disabledBecauseKeyDoesNotExist() { - } + @Disabled("Used by EnabledIfBuildConfigValueConditionTests only") + @EnabledIfBuildConfigValue(named = "NOT_EXISTENT_KEY", matches = "whatever") + @Test + fun disabledBecauseKeyDoesNotExist() {} } diff --git a/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/EnabledOnManufacturerConditionTests.kt b/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/EnabledOnManufacturerConditionTests.kt index dc931911..4c4f9c96 100644 --- a/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/EnabledOnManufacturerConditionTests.kt +++ b/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/EnabledOnManufacturerConditionTests.kt @@ -11,85 +11,74 @@ import org.junit.platform.commons.PreconditionViolationException /** * Unit tests for [EnabledOnManufacturerCondition]. * - * This works together with [EnabledOnManufacturerIntegrationTests]: The test methods - * in both classes MUST be named identical. + * This works together with [EnabledOnManufacturerIntegrationTests]: The test methods in both + * classes MUST be named identical. */ class EnabledOnManufacturerConditionTests : AbstractExecutionConditionTests() { - override fun getExecutionCondition(): ExecutionCondition = - EnabledOnManufacturerCondition() + override fun getExecutionCondition(): ExecutionCondition = EnabledOnManufacturerCondition() - override fun getTestClass(): Class<*> = EnabledOnManufacturerIntegrationTests::class.java + override fun getTestClass(): Class<*> = EnabledOnManufacturerIntegrationTests::class.java - /** - * @see [EnabledOnManufacturerIntegrationTests.invalidBecauseArrayIsEmpty] - */ - @Test - fun invalidBecauseArrayIsEmpty() { - val expected = assertThrows { - evaluateCondition() - } + /** @see [EnabledOnManufacturerIntegrationTests.invalidBecauseArrayIsEmpty] */ + @Test + fun invalidBecauseArrayIsEmpty() { + val expected = assertThrows { evaluateCondition() } - assertThat(expected).hasMessageThat().contains("You must declare at least one Manufacturer in @EnabledOnManufacturer") - } + assertThat(expected) + .hasMessageThat() + .contains("You must declare at least one Manufacturer in @EnabledOnManufacturer") + } - /** - * @see [EnabledOnManufacturerIntegrationTests.enabledBecauseValueMatchesExactly] - */ - @Test - fun enabledBecauseValueMatchesExactly() { - withManufacturer("Samsung") { - evaluateCondition() - assertEnabled() - assertReasonEquals("Enabled on Manufacturer: Samsung") + /** @see [EnabledOnManufacturerIntegrationTests.enabledBecauseValueMatchesExactly] */ + @Test + fun enabledBecauseValueMatchesExactly() { + withManufacturer("Samsung") { + evaluateCondition() + assertEnabled() + assertReasonEquals("Enabled on Manufacturer: Samsung") + } } - } - /** - * @see [EnabledOnManufacturerIntegrationTests.enabledBecauseValueIsAmongTheValues] - */ - @Test - fun enabledBecauseValueIsAmongTheValues() { - withManufacturer("Huawei") { - evaluateCondition() - assertEnabled() - assertReasonEquals("Enabled on Manufacturer: Huawei") + /** @see [EnabledOnManufacturerIntegrationTests.enabledBecauseValueIsAmongTheValues] */ + @Test + fun enabledBecauseValueIsAmongTheValues() { + withManufacturer("Huawei") { + evaluateCondition() + assertEnabled() + assertReasonEquals("Enabled on Manufacturer: Huawei") + } } - } - /** - * @see [EnabledOnManufacturerIntegrationTests.enabledBecauseValueMatchesWithOfIgnoreCase] - */ - @Test - fun enabledBecauseValueMatchesWithOfIgnoreCase() { - withManufacturer("Samsung") { - evaluateCondition() - assertEnabled() - assertReasonEquals("Enabled on Manufacturer: Samsung") + /** @see [EnabledOnManufacturerIntegrationTests.enabledBecauseValueMatchesWithOfIgnoreCase] */ + @Test + fun enabledBecauseValueMatchesWithOfIgnoreCase() { + withManufacturer("Samsung") { + evaluateCondition() + assertEnabled() + assertReasonEquals("Enabled on Manufacturer: Samsung") + } } - } - /** - * @see [EnabledOnManufacturerIntegrationTests.disabledBecauseValueDoesntMatchDueToIgnoreCase] - */ - @Test - fun disabledBecauseValueDoesntMatchDueToIgnoreCase() { - withManufacturer("Samsung") { - evaluateCondition() - assertDisabled() - assertReasonEquals("Disabled on Manufacturer: Samsung") + /** + * @see [EnabledOnManufacturerIntegrationTests.disabledBecauseValueDoesntMatchDueToIgnoreCase] + */ + @Test + fun disabledBecauseValueDoesntMatchDueToIgnoreCase() { + withManufacturer("Samsung") { + evaluateCondition() + assertDisabled() + assertReasonEquals("Disabled on Manufacturer: Samsung") + } } - } - /** - * @see [EnabledOnManufacturerIntegrationTests.disabledBecauseValueDoesntMatchAnyValue] - */ - @Test - fun disabledBecauseValueDoesntMatchAnyValue() { - withManufacturer("Google") { - evaluateCondition() - assertDisabled() - assertReasonEquals("Disabled on Manufacturer: Google") + /** @see [EnabledOnManufacturerIntegrationTests.disabledBecauseValueDoesntMatchAnyValue] */ + @Test + fun disabledBecauseValueDoesntMatchAnyValue() { + withManufacturer("Google") { + evaluateCondition() + assertDisabled() + assertReasonEquals("Disabled on Manufacturer: Google") + } } - } } diff --git a/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/EnabledOnManufacturerIntegrationTests.kt b/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/EnabledOnManufacturerIntegrationTests.kt index 146fa01a..4d1e0ab4 100644 --- a/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/EnabledOnManufacturerIntegrationTests.kt +++ b/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/EnabledOnManufacturerIntegrationTests.kt @@ -4,45 +4,39 @@ import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test /** - * Companion class for [EnabledOnManufacturerConditionTests]. - * The tests in here are intentionally disabled; the partner class will - * drive them through reflection in order to assert the behavior of the condition + * Companion class for [EnabledOnManufacturerConditionTests]. The tests in here are intentionally + * disabled; the partner class will drive them through reflection in order to assert the behavior of + * the condition */ class EnabledOnManufacturerIntegrationTests { - @Disabled("Used by EnabledOnManufacturerConditionTests only") - @EnabledOnManufacturer([]) - @Test - fun invalidBecauseArrayIsEmpty() { - } - - @Disabled("Used by EnabledOnManufacturerConditionTests only") - @EnabledOnManufacturer(["Samsung"]) - @Test - fun enabledBecauseValueMatchesExactly() { - } - - @Disabled("Used by EnabledOnManufacturerConditionTests only") - @EnabledOnManufacturer(["Samsung", "Huawei"]) - @Test - fun enabledBecauseValueIsAmongTheValues() { - } - - @Disabled("Used by EnabledOnManufacturerConditionTests only") - @EnabledOnManufacturer(["sAmSuNg"]) - @Test - fun enabledBecauseValueMatchesWithOfIgnoreCase() { - } - - @Disabled("Used by EnabledOnManufacturerConditionTests only") - @EnabledOnManufacturer(["sAmSuNg"], ignoreCase = false) - @Test - fun disabledBecauseValueDoesntMatchDueToIgnoreCase() { - } - - @Disabled("Used by EnabledOnManufacturerConditionTests only") - @EnabledOnManufacturer(["Samsung", "Huawei"]) - @Test - fun disabledBecauseValueDoesntMatchAnyValue() { - } + @Disabled("Used by EnabledOnManufacturerConditionTests only") + @EnabledOnManufacturer([]) + @Test + fun invalidBecauseArrayIsEmpty() {} + + @Disabled("Used by EnabledOnManufacturerConditionTests only") + @EnabledOnManufacturer(["Samsung"]) + @Test + fun enabledBecauseValueMatchesExactly() {} + + @Disabled("Used by EnabledOnManufacturerConditionTests only") + @EnabledOnManufacturer(["Samsung", "Huawei"]) + @Test + fun enabledBecauseValueIsAmongTheValues() {} + + @Disabled("Used by EnabledOnManufacturerConditionTests only") + @EnabledOnManufacturer(["sAmSuNg"]) + @Test + fun enabledBecauseValueMatchesWithOfIgnoreCase() {} + + @Disabled("Used by EnabledOnManufacturerConditionTests only") + @EnabledOnManufacturer(["sAmSuNg"], ignoreCase = false) + @Test + fun disabledBecauseValueDoesntMatchDueToIgnoreCase() {} + + @Disabled("Used by EnabledOnManufacturerConditionTests only") + @EnabledOnManufacturer(["Samsung", "Huawei"]) + @Test + fun disabledBecauseValueDoesntMatchAnyValue() {} } diff --git a/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/EnabledOnSdkVersionConditionTests.kt b/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/EnabledOnSdkVersionConditionTests.kt index ea72c6b1..1dded892 100644 --- a/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/EnabledOnSdkVersionConditionTests.kt +++ b/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/EnabledOnSdkVersionConditionTests.kt @@ -11,96 +11,82 @@ import org.junit.platform.commons.PreconditionViolationException /** * Unit tests for [EnabledOnSdkVersionCondition]. * - * This works together with [EnabledOnSdkVersionIntegrationTests]: The test methods - * in both classes MUST be named identical. + * This works together with [EnabledOnSdkVersionIntegrationTests]: The test methods in both classes + * MUST be named identical. */ class EnabledOnSdkVersionConditionTests : AbstractExecutionConditionTests() { - override fun getExecutionCondition(): ExecutionCondition = EnabledOnSdkVersionCondition() + override fun getExecutionCondition(): ExecutionCondition = EnabledOnSdkVersionCondition() - override fun getTestClass(): Class<*> = EnabledOnSdkVersionIntegrationTests::class.java + override fun getTestClass(): Class<*> = EnabledOnSdkVersionIntegrationTests::class.java - /** - * @see [EnabledOnSdkVersionIntegrationTests.invalidBecauseNoValueGiven] - */ - @Test - fun invalidBecauseNoValueGiven() { - val expected = assertThrows { - evaluateCondition() - } + /** @see [EnabledOnSdkVersionIntegrationTests.invalidBecauseNoValueGiven] */ + @Test + fun invalidBecauseNoValueGiven() { + val expected = assertThrows { evaluateCondition() } - assertThat(expected).hasMessageThat().contains("At least one value must be provided in @EnabledOnSdkVersion") - } + assertThat(expected) + .hasMessageThat() + .contains("At least one value must be provided in @EnabledOnSdkVersion") + } - /** - * @see [EnabledOnSdkVersionIntegrationTests.enabledBecauseMinApiIsMatched] - */ - @Test - fun enabledBecauseMinApiIsMatched() { - withApiLevel(26) { - evaluateCondition() - assertEnabled() - assertReasonEquals("Enabled on API 26") + /** @see [EnabledOnSdkVersionIntegrationTests.enabledBecauseMinApiIsMatched] */ + @Test + fun enabledBecauseMinApiIsMatched() { + withApiLevel(26) { + evaluateCondition() + assertEnabled() + assertReasonEquals("Enabled on API 26") + } } - } - /** - * @see [EnabledOnSdkVersionIntegrationTests.enabledBecauseMaxApiIsMatched] - */ - @Test - fun enabledBecauseMaxApiIsMatched() { - withApiLevel(24) { - evaluateCondition() - assertEnabled() - assertReasonEquals("Enabled on API 24") + /** @see [EnabledOnSdkVersionIntegrationTests.enabledBecauseMaxApiIsMatched] */ + @Test + fun enabledBecauseMaxApiIsMatched() { + withApiLevel(24) { + evaluateCondition() + assertEnabled() + assertReasonEquals("Enabled on API 24") + } } - } - /** - * @see [EnabledOnSdkVersionIntegrationTests.enabledBecauseApiIsInValidRange] - */ - @Test - fun enabledBecauseApiIsInValidRange() { - withApiLevel(26) { - evaluateCondition() - assertEnabled() - assertReasonEquals("Enabled on API 26") + /** @see [EnabledOnSdkVersionIntegrationTests.enabledBecauseApiIsInValidRange] */ + @Test + fun enabledBecauseApiIsInValidRange() { + withApiLevel(26) { + evaluateCondition() + assertEnabled() + assertReasonEquals("Enabled on API 26") + } } - } - /** - * @see [EnabledOnSdkVersionIntegrationTests.disabledBecauseMinApiTooLow] - */ - @Test - fun disabledBecauseMinApiTooLow() { - withApiLevel(26) { - evaluateCondition() - assertDisabled() - assertReasonEquals("Disabled on API 26") + /** @see [EnabledOnSdkVersionIntegrationTests.disabledBecauseMinApiTooLow] */ + @Test + fun disabledBecauseMinApiTooLow() { + withApiLevel(26) { + evaluateCondition() + assertDisabled() + assertReasonEquals("Disabled on API 26") + } } - } - /** - * @see [EnabledOnSdkVersionIntegrationTests.disabledBecauseMaxApiTooHigh] - */ - @Test - fun disabledBecauseMaxApiTooHigh() { - withApiLevel(29) { - evaluateCondition() - assertDisabled() - assertReasonEquals("Disabled on API 29") + /** @see [EnabledOnSdkVersionIntegrationTests.disabledBecauseMaxApiTooHigh] */ + @Test + fun disabledBecauseMaxApiTooHigh() { + withApiLevel(29) { + evaluateCondition() + assertDisabled() + assertReasonEquals("Disabled on API 29") + } } - } - /** - * @see [EnabledOnSdkVersionIntegrationTests.disabledBecauseApiIsOutsideValidRange] - */ - @Test - fun disabledBecauseApiIsOutsideValidRange() { - withApiLevel(26) { - evaluateCondition() - assertDisabled() - assertReasonEquals("Disabled on API 26") + /** @see [EnabledOnSdkVersionIntegrationTests.disabledBecauseApiIsOutsideValidRange] */ + @Test + fun disabledBecauseApiIsOutsideValidRange() { + withApiLevel(26) { + evaluateCondition() + assertDisabled() + assertReasonEquals("Disabled on API 26") + } } - } } diff --git a/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/EnabledOnSdkVersionIntegrationTests.kt b/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/EnabledOnSdkVersionIntegrationTests.kt index 64fce9c2..e4a77eff 100644 --- a/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/EnabledOnSdkVersionIntegrationTests.kt +++ b/instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/EnabledOnSdkVersionIntegrationTests.kt @@ -4,51 +4,44 @@ import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test /** - * Companion class for [EnabledOnSdkVersionConditionTests]. - * The tests in here are intentionally disabled; the partner class will - * drive them through reflection in order to assert the behavior of the condition + * Companion class for [EnabledOnSdkVersionConditionTests]. The tests in here are intentionally + * disabled; the partner class will drive them through reflection in order to assert the behavior of + * the condition */ class EnabledOnSdkVersionIntegrationTests { - @Disabled("Used by EnabledOnSdkVersionConditionTests only") - @EnabledOnSdkVersion - @Test - fun invalidBecauseNoValueGiven() { - } - - @Disabled("Used by EnabledOnSdkVersionConditionTests only") - @EnabledOnSdkVersion(from = 24) - @Test - fun enabledBecauseMinApiIsMatched() { - } - - @Disabled("Used by EnabledOnSdkVersionConditionTests only") - @EnabledOnSdkVersion(until = 26) - @Test - fun enabledBecauseMaxApiIsMatched() { - } - - @Disabled("Used by EnabledOnSdkVersionConditionTests only") - @EnabledOnSdkVersion(from = 24, until = 29) - @Test - fun enabledBecauseApiIsInValidRange() { - } - - @Disabled("Used by EnabledOnSdkVersionConditionTests only") - @EnabledOnSdkVersion(from = 27) - @Test - fun disabledBecauseMinApiTooLow() { - } - - @Disabled("Used by EnabledOnSdkVersionConditionTests only") - @EnabledOnSdkVersion(until = 27) - @Test - fun disabledBecauseMaxApiTooHigh() { - } - - @Disabled("Used by EnabledOnSdkVersionConditionTests only") - @EnabledOnSdkVersion(from = 27, until = 29) - @Test - fun disabledBecauseApiIsOutsideValidRange() { - } + @Disabled("Used by EnabledOnSdkVersionConditionTests only") + @EnabledOnSdkVersion + @Test + fun invalidBecauseNoValueGiven() {} + + @Disabled("Used by EnabledOnSdkVersionConditionTests only") + @EnabledOnSdkVersion(from = 24) + @Test + fun enabledBecauseMinApiIsMatched() {} + + @Disabled("Used by EnabledOnSdkVersionConditionTests only") + @EnabledOnSdkVersion(until = 26) + @Test + fun enabledBecauseMaxApiIsMatched() {} + + @Disabled("Used by EnabledOnSdkVersionConditionTests only") + @EnabledOnSdkVersion(from = 24, until = 29) + @Test + fun enabledBecauseApiIsInValidRange() {} + + @Disabled("Used by EnabledOnSdkVersionConditionTests only") + @EnabledOnSdkVersion(from = 27) + @Test + fun disabledBecauseMinApiTooLow() {} + + @Disabled("Used by EnabledOnSdkVersionConditionTests only") + @EnabledOnSdkVersion(until = 27) + @Test + fun disabledBecauseMaxApiTooHigh() {} + + @Disabled("Used by EnabledOnSdkVersionConditionTests only") + @EnabledOnSdkVersion(from = 27, until = 29) + @Test + fun disabledBecauseApiIsOutsideValidRange() {} } diff --git a/instrumentation/core/src/test/java/de/mannodermaus/junit5/util/ResourceLocks.kt b/instrumentation/core/src/test/java/de/mannodermaus/junit5/util/ResourceLocks.kt index b23ba7c6..29c39b31 100644 --- a/instrumentation/core/src/test/java/de/mannodermaus/junit5/util/ResourceLocks.kt +++ b/instrumentation/core/src/test/java/de/mannodermaus/junit5/util/ResourceLocks.kt @@ -1,6 +1,4 @@ package de.mannodermaus.junit5.util -/** - * JUnit Jupiter resource locks, restricting parallelism of the test suite. - */ +/** JUnit Jupiter resource locks, restricting parallelism of the test suite. */ const val RESOURCE_LOCK_INSTRUMENTATION = "instrumentation" diff --git a/instrumentation/extensions/build.gradle.kts b/instrumentation/extensions/build.gradle.kts index 78f5642f..cd16206f 100644 --- a/instrumentation/extensions/build.gradle.kts +++ b/instrumentation/extensions/build.gradle.kts @@ -7,9 +7,7 @@ plugins { android { namespace = "de.mannodermaus.junit5.extensions" - defaultConfig { - minSdk = Android.testRunnerMinSdkVersion - } + defaultConfig { minSdk = Android.testRunnerMinSdkVersion } } dependencies { diff --git a/instrumentation/extensions/src/main/kotlin/de/mannodermaus/junit5/extensions/GrantPermissionExtension.kt b/instrumentation/extensions/src/main/kotlin/de/mannodermaus/junit5/extensions/GrantPermissionExtension.kt index ef65f627..e6b45937 100644 --- a/instrumentation/extensions/src/main/kotlin/de/mannodermaus/junit5/extensions/GrantPermissionExtension.kt +++ b/instrumentation/extensions/src/main/kotlin/de/mannodermaus/junit5/extensions/GrantPermissionExtension.kt @@ -2,8 +2,6 @@ package de.mannodermaus.junit5.extensions import android.Manifest import android.annotation.SuppressLint -import android.os.Build -import androidx.test.annotation.ExperimentalTestApi import androidx.test.internal.platform.ServiceLoaderWrapper.loadSingleService import androidx.test.internal.platform.content.PermissionGranter import androidx.test.runner.permission.PermissionRequester @@ -12,16 +10,16 @@ import org.junit.jupiter.api.extension.BeforeEachCallback import org.junit.jupiter.api.extension.ExtensionContext /** - * The [GrantPermissionExtension] allows granting of runtime permissions before a test. - * Use this extension when a test requires a runtime permission to do its work. + * The [GrantPermissionExtension] allows granting of runtime permissions before a test. Use this + * extension when a test requires a runtime permission to do its work. * * This is a port of JUnit 4's GrantPermissionRule for JUnit 5. * - *

When applied to a test class it attempts to grant all requested runtime permissions. - * The requested permissions will then be granted on the device and will take immediate effect. - * Once a permission is granted it will apply for all tests running in the current Instrumentation. - * There is no way of revoking a permission after it was granted. - * Attempting to do so will crash the Instrumentation process. + *

When applied to a test class it attempts to grant all requested runtime permissions. The + * requested permissions will then be granted on the device and will take immediate effect. Once a + * permission is granted it will apply for all tests running in the current Instrumentation. There + * is no way of revoking a permission after it was granted. Attempting to do so will crash the + * Instrumentation process. */ @SuppressLint("RestrictedApi") public class GrantPermissionExtension @@ -40,9 +38,7 @@ internal constructor(private val permissionGranter: PermissionGranter) : BeforeE public fun grant(vararg permissions: String): GrantPermissionExtension { val granter = loadSingleService(PermissionGranter::class.java, ::PermissionRequester) - return GrantPermissionExtension(granter).also { - it.grantPermissions(permissions) - } + return GrantPermissionExtension(granter).also { it.grantPermissions(permissions) } } private fun satisfyPermissionDependencies(permissions: Array): Set { diff --git a/instrumentation/extensions/src/test/kotlin/de/mannodermaus/junit5/extensions/GrantPermissionExtensionTests.kt b/instrumentation/extensions/src/test/kotlin/de/mannodermaus/junit5/extensions/GrantPermissionExtensionTests.kt index 5b28b015..40958fe5 100644 --- a/instrumentation/extensions/src/test/kotlin/de/mannodermaus/junit5/extensions/GrantPermissionExtensionTests.kt +++ b/instrumentation/extensions/src/test/kotlin/de/mannodermaus/junit5/extensions/GrantPermissionExtensionTests.kt @@ -5,13 +5,13 @@ import android.os.Build import androidx.test.internal.platform.content.PermissionGranter import com.google.common.truth.Truth.assertThat import de.mannodermaus.junit5.testutil.AndroidBuildUtils.withApiLevel +import java.lang.reflect.Modifier import org.junit.jupiter.api.DynamicTest import org.junit.jupiter.api.DynamicTest.dynamicTest import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestFactory import org.junit.jupiter.api.extension.ExtensionContext import org.mockito.Mockito.mock -import java.lang.reflect.Modifier class GrantPermissionExtensionTests { @@ -21,8 +21,7 @@ class GrantPermissionExtensionTests { fun `single permission`() { runExtension(Manifest.permission.CAMERA) - assertThat(granter.grantedPermissions) - .containsExactly(Manifest.permission.CAMERA) + assertThat(granter.grantedPermissions).containsExactly(Manifest.permission.CAMERA) } @Test @@ -36,7 +35,8 @@ class GrantPermissionExtensionTests { .containsExactly( Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, - ).inOrder() + ) + .inOrder() } @TestFactory @@ -53,7 +53,8 @@ class GrantPermissionExtensionTests { .containsExactly( Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE, - ).inOrder() + ) + .inOrder() } } } @@ -65,7 +66,9 @@ class GrantPermissionExtensionTests { // Look inside Build.VERSION_CODES and locate // the static int field with the highest value, // except for the 'CUR_DEVELOPMENT' test field - return Build.VERSION_CODES::class.java.declaredFields + return Build.VERSION_CODES::class + .java + .declaredFields .filter { Modifier.isStatic(it.modifiers) } .filter { it.type == Int::class.java } .filter { it.name != "CUR_DEVELOPMENT" } diff --git a/instrumentation/runner/build.gradle.kts b/instrumentation/runner/build.gradle.kts index 3f7f3aa7..cc6ffbd5 100644 --- a/instrumentation/runner/build.gradle.kts +++ b/instrumentation/runner/build.gradle.kts @@ -7,9 +7,7 @@ plugins { android { namespace = "de.mannodermaus.junit5.runner" - defaultConfig { - minSdk = Android.testRunnerMinSdkVersion - } + defaultConfig { minSdk = Android.testRunnerMinSdkVersion } } configurations.all { diff --git a/instrumentation/runner/src/five/kotlin/de/mannodermaus/junit5/internal/RunnerConstants.kt b/instrumentation/runner/src/five/kotlin/de/mannodermaus/junit5/internal/RunnerConstants.kt index 4cbe229f..2fd986ba 100644 --- a/instrumentation/runner/src/five/kotlin/de/mannodermaus/junit5/internal/RunnerConstants.kt +++ b/instrumentation/runner/src/five/kotlin/de/mannodermaus/junit5/internal/RunnerConstants.kt @@ -1,7 +1,7 @@ package de.mannodermaus.junit5.internal /** - * The minimum Android API level on which JUnit Framework tests may be executed. - * Trying to launch a test on an older device will simply mark it as 'skipped'. + * The minimum Android API level on which JUnit Framework tests may be executed. Trying to launch a + * test on an older device will simply mark it as 'skipped'. */ internal const val JUNIT_FRAMEWORK_MINIMUM_SDK_VERSION: Int = 26 diff --git a/instrumentation/runner/src/five/kotlin/de/mannodermaus/junit5/internal/discovery/EmptyConfigurationParameters.kt b/instrumentation/runner/src/five/kotlin/de/mannodermaus/junit5/internal/discovery/EmptyConfigurationParameters.kt index e4a3b731..6b4b1362 100644 --- a/instrumentation/runner/src/five/kotlin/de/mannodermaus/junit5/internal/discovery/EmptyConfigurationParameters.kt +++ b/instrumentation/runner/src/five/kotlin/de/mannodermaus/junit5/internal/discovery/EmptyConfigurationParameters.kt @@ -1,19 +1,20 @@ package de.mannodermaus.junit5.internal.discovery import androidx.annotation.RequiresApi -import org.junit.platform.engine.ConfigurationParameters import java.util.Optional +import org.junit.platform.engine.ConfigurationParameters /** - * JUnit 5 version of the [ConfigurationParameters] interface, - * including the deprecated APIs that were removed in subsequent versions of the framework. + * JUnit 5 version of the [ConfigurationParameters] interface, including the deprecated APIs that + * were removed in subsequent versions of the framework. */ @RequiresApi(26) internal object EmptyConfigurationParameters : ConfigurationParameters { override fun get(key: String) = Optional.empty() + override fun getBoolean(key: String) = Optional.empty() + override fun keySet() = emptySet() - @Deprecated("Deprecated in Java", ReplaceWith("keySet().size")) - override fun size() = 0 + @Deprecated("Deprecated in Java", ReplaceWith("keySet().size")) override fun size() = 0 } diff --git a/instrumentation/runner/src/five/kotlin/de/mannodermaus/junit5/internal/runners/TestPlanAdapter.kt b/instrumentation/runner/src/five/kotlin/de/mannodermaus/junit5/internal/runners/TestPlanAdapter.kt index d56eeee1..6c42afa8 100644 --- a/instrumentation/runner/src/five/kotlin/de/mannodermaus/junit5/internal/runners/TestPlanAdapter.kt +++ b/instrumentation/runner/src/five/kotlin/de/mannodermaus/junit5/internal/runners/TestPlanAdapter.kt @@ -4,16 +4,15 @@ import org.junit.platform.launcher.TestIdentifier import org.junit.platform.launcher.TestPlan /** - * JUnit 5 version of the [TestPlanAdapter], - * including the deprecated APIs that were removed in subsequent versions of the framework. + * JUnit 5 version of the [TestPlanAdapter], including the deprecated APIs that were removed in + * subsequent versions of the framework. */ -internal open class TestPlanAdapter( - val delegate: TestPlan -) : TestPlan( - /* containsTests = */ delegate.containsTests(), - /* configurationParameters = */ delegate.configurationParameters, - /* outputDirectoryCreator = */ delegate.outputDirectoryCreator -) { +internal open class TestPlanAdapter(val delegate: TestPlan) : + TestPlan( + /* containsTests = */ delegate.containsTests(), + /* configurationParameters = */ delegate.configurationParameters, + /* outputDirectoryCreator = */ delegate.outputDirectoryCreator, + ) { @Deprecated("Deprecated in Java") @Suppress("DEPRECATION") override fun getChildren(parentId: String): Set { diff --git a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/AndroidJUnitFrameworkBuilder.kt b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/AndroidJUnitFrameworkBuilder.kt index d5834aec..243c5e42 100644 --- a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/AndroidJUnitFrameworkBuilder.kt +++ b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/AndroidJUnitFrameworkBuilder.kt @@ -9,15 +9,13 @@ import org.junit.runner.Runner import org.junit.runners.model.RunnerBuilder /** - * Custom RunnerBuilder hooked into the main Test Instrumentation Runner - * provided by the Android Test Support Library, which allows to run - * the JUnit Platform for instrumented tests. With this, - * the default JUnit 4-based Runner for Android instrumented tests is, - * in a way, tricked into detecting JUnit Jupiter tests as well. - * - * The RunnerBuilder is added to the instrumentation runner - * through a custom "testInstrumentationRunnerArgument" in the build.gradle script: + * Custom RunnerBuilder hooked into the main Test Instrumentation Runner provided by the Android + * Test Support Library, which allows to run the JUnit Platform for instrumented tests. With this, + * the default JUnit 4-based Runner for Android instrumented tests is, in a way, tricked into + * detecting JUnit Jupiter tests as well. * + * The RunnerBuilder is added to the instrumentation runner through a custom + * "testInstrumentationRunnerArgument" in the build.gradle script: *

  *   android {
  *     defaultConfig {
@@ -27,8 +25,8 @@ import org.junit.runners.model.RunnerBuilder
  *   }
  * 
* - * (Suppressing unused, since this is hooked into the - * project configuration via a Test Instrumentation Runner Argument.) + * (Suppressing unused, since this is hooked into the project configuration via a Test + * Instrumentation Runner Argument.) */ public open class AndroidJUnitFrameworkBuilder internal constructor() : RunnerBuilder() { private val junitFrameworkAvailable by lazy { @@ -73,9 +71,9 @@ public open class AndroidJUnitFrameworkBuilder internal constructor() : RunnerBu Log.e(LOG_TAG, "JUnitPlatform not found on runtime classpath") throw IllegalStateException( "junit-platform-runner not found on runtime classpath of instrumentation tests; " + - "please review your androidTest dependencies or raise an issue.", e + "please review your androidTest dependencies or raise an issue.", + e, ) - } catch (e: Throwable) { Log.e(LOG_TAG, "Error constructing runner", e) throw e @@ -84,18 +82,13 @@ public open class AndroidJUnitFrameworkBuilder internal constructor() : RunnerBu /* Private */ - private val ignorablePackages = setOf( - "java.", - "javax.", - "androidx.", - "com.android.", - "kotlin.", - "kotlinx.", - ) + private val ignorablePackages = + setOf("java.", "javax.", "androidx.", "com.android.", "kotlin.", "kotlinx.") - private val Class<*>.isInIgnorablePackage: Boolean get() { - return ignorablePackages.any { name.startsWith(it) } - } + private val Class<*>.isInIgnorablePackage: Boolean + get() { + return ignorablePackages.any { name.startsWith(it) } + } private fun JUnitFrameworkRunnerParams.registerEnvironmentVariables() { environmentVariables.forEach { (key, value) -> @@ -119,15 +112,13 @@ public open class AndroidJUnitFrameworkBuilder internal constructor() : RunnerBu } /** - * Custom RunnerBuilder hooked into the main Test Instrumentation Runner - * provided by the Android Test Support Library, which allows to run - * the JUnit Platform for instrumented tests. With this, - * the default JUnit 4-based Runner for Android instrumented tests is, - * in a way, tricked into detecting JUnit Jupiter tests as well. - * - * The RunnerBuilder is added to the instrumentation runner - * through a custom "testInstrumentationRunnerArgument" in the build.gradle script: + * Custom RunnerBuilder hooked into the main Test Instrumentation Runner provided by the Android + * Test Support Library, which allows to run the JUnit Platform for instrumented tests. With this, + * the default JUnit 4-based Runner for Android instrumented tests is, in a way, tricked into + * detecting JUnit Jupiter tests as well. * + * The RunnerBuilder is added to the instrumentation runner through a custom + * "testInstrumentationRunnerArgument" in the build.gradle script: *
  *   android {
  *     defaultConfig {
@@ -137,11 +128,11 @@ public open class AndroidJUnitFrameworkBuilder internal constructor() : RunnerBu
  *   }
  * 
* - * (Suppressing unused, since this is hooked into the - * project configuration via a Test Instrumentation Runner Argument.) + * (Suppressing unused, since this is hooked into the project configuration via a Test + * Instrumentation Runner Argument.) */ @Deprecated( message = "Renamed to AndroidJUnitFrameworkBuilder", - replaceWith = ReplaceWith("AndroidJUnitFrameworkBuilder") + replaceWith = ReplaceWith("AndroidJUnitFrameworkBuilder"), ) public class AndroidJUnit5Builder : AndroidJUnitFrameworkBuilder() diff --git a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/LibcoreAccess.kt b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/LibcoreAccess.kt index 63e89657..6f718ac1 100644 --- a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/LibcoreAccess.kt +++ b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/LibcoreAccess.kt @@ -7,12 +7,13 @@ internal object LibcoreAccess { private class Wrapper { private val libcoreClass = Class.forName("libcore.io.Libcore") private val libcoreOsObject = libcoreClass.getField("os").get(null) - private val setEnvMethod = libcoreOsObject.javaClass.getMethod( - "setenv", - String::class.java, - String::class.java, - Boolean::class.java - ) + private val setEnvMethod = + libcoreOsObject.javaClass.getMethod( + "setenv", + String::class.java, + String::class.java, + Boolean::class.java, + ) fun setenv(key: String, value: String, overwrite: Boolean) { setEnvMethod.invoke(libcoreOsObject, key, value, overwrite) @@ -29,11 +30,12 @@ internal object LibcoreAccess { } /** - * Invokes the method "libcore.io.Libcore.os.setenv(String, String)" with the provided key/value pair. - * This effectively adds a custom environment variable to the running process, - * allowing instrumentation tests to honor JUnit 5's @EnabledIfEnvironmentVariable and @DisabledIfEnvironmentVariable annotations. + * Invokes the method "libcore.io.Libcore.os.setenv(String, String)" with the provided key/value + * pair. This effectively adds a custom environment variable to the running process, allowing + * instrumentation tests to honor JUnit 5's @EnabledIfEnvironmentVariable + * and @DisabledIfEnvironmentVariable annotations. * - * @param key Key of the variable + * @param key Key of the variable * @param value Value of the variable * @throws IllegalAccessException If Libcore is not available */ diff --git a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/discovery/EmptyTestPlan.kt b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/discovery/EmptyTestPlan.kt index f3d44c2d..0ef81ada 100644 --- a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/discovery/EmptyTestPlan.kt +++ b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/discovery/EmptyTestPlan.kt @@ -1,26 +1,30 @@ package de.mannodermaus.junit5.internal.discovery import androidx.annotation.RequiresApi +import java.io.File import org.junit.platform.engine.OutputDirectoryCreator import org.junit.platform.engine.TestDescriptor import org.junit.platform.launcher.TestPlan -import java.io.File /** - * A JUnit TestPlan that does absolutely nothing. - * Used by [de.mannodermaus.junit5.internal.runners.AndroidJUnitFramework] whenever a class - * is not loadable through the JUnit Platform and should be discarded. + * A JUnit TestPlan that does absolutely nothing. Used by + * [de.mannodermaus.junit5.internal.runners.AndroidJUnitFramework] whenever a class is not loadable + * through the JUnit Platform and should be discarded. */ @RequiresApi(26) -internal object EmptyTestPlan : TestPlan( - /* containsTests = */ false, - /* configurationParameters = */ EmptyConfigurationParameters, - /* outputDirectoryCreator = */ emptyOutputDirectoryCreator -) +internal object EmptyTestPlan : + TestPlan( + /* containsTests = */ false, + /* configurationParameters = */ EmptyConfigurationParameters, + /* outputDirectoryCreator = */ emptyOutputDirectoryCreator, + ) @RequiresApi(26) -private val emptyOutputDirectoryCreator = object : OutputDirectoryCreator { - private val path = File.createTempFile("empty-output", ".nop").toPath() - override fun getRootDirectory() = path - override fun createOutputDirectory(testDescriptor: TestDescriptor) = path -} +private val emptyOutputDirectoryCreator = + object : OutputDirectoryCreator { + private val path = File.createTempFile("empty-output", ".nop").toPath() + + override fun getRootDirectory() = path + + override fun createOutputDirectory(testDescriptor: TestDescriptor) = path + } diff --git a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/discovery/GeneratedFilters.kt b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/discovery/GeneratedFilters.kt index 2104f9b7..cacebdc7 100644 --- a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/discovery/GeneratedFilters.kt +++ b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/discovery/GeneratedFilters.kt @@ -9,9 +9,8 @@ import org.junit.platform.launcher.TagFilter private const val INSTRUMENTATION_FILTER_RES_FILE_NAME = "de_mannodermaus_junit5_filters" /** - * Holder object for the filters of a test plan. - * It converts the contents of a resource file into JUnit Platform [Filter] objects - * for the [AndroidJUnitFramework] runner. + * Holder object for the filters of a test plan. It converts the contents of a resource file into + * JUnit Platform [Filter] objects for the [AndroidJUnitFramework] runner. */ internal object GeneratedFilters { @@ -21,21 +20,23 @@ internal object GeneratedFilters { // Look up the resource file written by the Gradle plugin // and open it. // (See Constants.kt inside the plugin's repository for the value used here) - val identifier = context.resources.getIdentifier( - INSTRUMENTATION_FILTER_RES_FILE_NAME, - "raw", - context.packageName - ) - val inputStream = if (identifier != 0) { - try { - context.resources.openRawResource(identifier) - } catch (_: Resources.NotFoundException) { - // Ignore + val identifier = + context.resources.getIdentifier( + INSTRUMENTATION_FILTER_RES_FILE_NAME, + "raw", + context.packageName, + ) + val inputStream = + if (identifier != 0) { + try { + context.resources.openRawResource(identifier) + } catch (_: Resources.NotFoundException) { + // Ignore + null + } + } else { null } - } else { - null - } if (inputStream == null) { // File doesn't exist, or couldn't be located; return diff --git a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/discovery/ParsedSelectors.kt b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/discovery/ParsedSelectors.kt index 3afbe5f3..61999c78 100644 --- a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/discovery/ParsedSelectors.kt +++ b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/discovery/ParsedSelectors.kt @@ -6,10 +6,9 @@ import org.junit.platform.engine.DiscoverySelector import org.junit.platform.engine.discovery.DiscoverySelectors /** - * Holder object for the selectors of a test plan. - * It converts the arguments handed to the Runner by the - * Android instrumentation into JUnit Platform [DiscoverySelector] objects - * for the [AndroidJUnitFramework] runner. + * Holder object for the selectors of a test plan. It converts the arguments handed to the Runner by + * the Android instrumentation into JUnit Platform [DiscoverySelector] objects for the + * [AndroidJUnitFramework] runner. */ internal object ParsedSelectors { fun fromBundle(testClass: Class<*>, arguments: Bundle): List { @@ -23,29 +22,31 @@ internal object ParsedSelectors { // Separate the provided argument into methods, if any are given // (Format: class=com.package1.FirstTest#method1,com.package1.SecondTest#method2). // For each component in this string that applies to the test class at hand, - // consider it a method filter if the name is appended to the component, using a pound sign (#). - // Finally, if at least one of these method filters can be found, construct JUnit selectors from it - classArg.split(",") - .forEach { component -> - if (!component.startsWith(testClassName)) { - // Not the desired class - return@forEach - } - - // Try extracting an appended method name - var methodName = component.replace(testClassName, "") - if (!methodName.startsWith("#")) { - return@forEach - } - methodName = methodName.substring(1) - - // Find all methods with the given name - val eligibleMethods = methods + // consider it a method filter if the name is appended to the component, using a pound + // sign (#). + // Finally, if at least one of these method filters can be found, construct JUnit + // selectors from it + classArg.split(",").forEach { component -> + if (!component.startsWith(testClassName)) { + // Not the desired class + return@forEach + } + + // Try extracting an appended method name + var methodName = component.replace(testClassName, "") + if (!methodName.startsWith("#")) { + return@forEach + } + methodName = methodName.substring(1) + + // Find all methods with the given name + val eligibleMethods = + methods .filter { it.name == methodName } .map { method -> DiscoverySelectors.selectMethod(testClass, method) } - selectors += eligibleMethods - } + selectors += eligibleMethods + } if (selectors.isNotEmpty()) { // Restrictions to specific methods apply diff --git a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/discovery/PropertiesParser.kt b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/discovery/PropertiesParser.kt index e69ab49c..fdfd9436 100644 --- a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/discovery/PropertiesParser.kt +++ b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/discovery/PropertiesParser.kt @@ -2,7 +2,8 @@ package de.mannodermaus.junit5.internal.discovery internal object PropertiesParser { fun fromString(string: String) = - string.split(",") + string + .split(",") .map { keyValuePair -> keyValuePair.split("=") } .filter { keyValueList -> keyValueList.size == 2 } .associate { it[0] to it[1] } diff --git a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/discovery/ShardingFilter.kt b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/discovery/ShardingFilter.kt index 87ddefc3..38d87d5f 100644 --- a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/discovery/ShardingFilter.kt +++ b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/discovery/ShardingFilter.kt @@ -2,23 +2,21 @@ package de.mannodermaus.junit5.internal.discovery import android.os.Bundle import de.mannodermaus.junit5.internal.extensions.isDynamicTest +import kotlin.math.abs import org.junit.platform.engine.FilterResult import org.junit.platform.engine.TestDescriptor import org.junit.platform.launcher.PostDiscoveryFilter import org.junit.platform.launcher.TestIdentifier -import kotlin.math.abs /** * JUnit 5 implementation of the default instrumentation's - * `androidx.test.internal.runner.TestRequestBuilder$ShardingFilter`, - * ported to the new API to support dynamic test templates, too. + * `androidx.test.internal.runner.TestRequestBuilder$ShardingFilter`, ported to the new API to + * support dynamic test templates, too. * * Based on a draft by KyoungJoo Jeon (@jkj8790). */ -internal class ShardingFilter( - private val numShards: Int, - private val shardIndex: Int, -) : PostDiscoveryFilter { +internal class ShardingFilter(private val numShards: Int, private val shardIndex: Int) : + PostDiscoveryFilter { companion object { private const val ARG_NUM_SHARDS = "numShards" diff --git a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/dummy/JupiterTestMethodFinder.kt b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/dummy/JupiterTestMethodFinder.kt index 8109c8ef..6eeb934f 100644 --- a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/dummy/JupiterTestMethodFinder.kt +++ b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/dummy/JupiterTestMethodFinder.kt @@ -2,18 +2,18 @@ package de.mannodermaus.junit5.internal.dummy import android.util.Log import de.mannodermaus.junit5.internal.LOG_TAG +import java.lang.reflect.Method +import java.lang.reflect.Modifier import org.junit.jupiter.api.RepeatedTest import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestFactory import org.junit.jupiter.api.TestTemplate import org.junit.jupiter.params.ParameterizedTest -import java.lang.reflect.Method -import java.lang.reflect.Modifier /** - * Algorithm to find all methods annotated with a JUnit Jupiter annotation - * for devices running below the API level requirement of the JUnit Framework. - * As they rely on Java 8 stuff, we're unable to rely on JUnit Platform's own reflection utilities. + * Algorithm to find all methods annotated with a JUnit Jupiter annotation for devices running below + * the API level requirement of the JUnit Framework. As they rely on Java 8 stuff, we're unable to + * rely on JUnit Platform's own reflection utilities. */ internal object JupiterTestMethodFinder { // Carefully access the Jupiter annotations, since it's possible that they aren't on @@ -57,7 +57,7 @@ internal object JupiterTestMethodFinder { Log.w( LOG_TAG, "Encountered ${t.javaClass.simpleName} while finding Jupiter test methods for ${this@doFind.name}", - t + t, ) } } diff --git a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/extensions/TestIdentifierExt.kt b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/extensions/TestIdentifierExt.kt index c54aeb90..7ea7600d 100644 --- a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/extensions/TestIdentifierExt.kt +++ b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/extensions/TestIdentifierExt.kt @@ -3,13 +3,14 @@ package de.mannodermaus.junit5.internal.extensions import de.mannodermaus.junit5.internal.formatters.TestNameFormatter import org.junit.platform.launcher.TestIdentifier -private val DYNAMIC_TEST_PREFIXES = listOf( - "[test-template-invocation", - "[dynamic-test", - "[dynamic-container", - "[test-factory", - "[test-template" -) +private val DYNAMIC_TEST_PREFIXES = + listOf( + "[test-template-invocation", + "[dynamic-test", + "[dynamic-container", + "[test-factory", + "[test-template", + ) private val TestIdentifier.shortId: String get() { @@ -22,8 +23,8 @@ private val TestIdentifier.shortId: String } /** - * Check if the given TestIdentifier describes a "test template invocation", - * i.e. a dynamic test generated at runtime. + * Check if the given TestIdentifier describes a "test template invocation", i.e. a dynamic test + * generated at runtime. */ internal val TestIdentifier.isDynamicTest: Boolean get() { @@ -32,10 +33,8 @@ internal val TestIdentifier.isDynamicTest: Boolean } /** - * Returns a formatted version of this identifier's name, - * which is compatible with the quirks and limitations - * of the Android Instrumentation, esp. when the [legacyFormat] - * flag is enabled. + * Returns a formatted version of this identifier's name, which is compatible with the quirks and + * limitations of the Android Instrumentation, esp. when the [legacyFormat] flag is enabled. */ internal fun TestIdentifier.format(legacyFormat: Boolean = false): String = TestNameFormatter.format(this, legacyFormat) diff --git a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/formatters/TestNameFormatter.kt b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/formatters/TestNameFormatter.kt index 40acf8e9..018d60f2 100644 --- a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/formatters/TestNameFormatter.kt +++ b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/formatters/TestNameFormatter.kt @@ -3,22 +3,24 @@ package de.mannodermaus.junit5.internal.formatters import org.junit.platform.launcher.TestIdentifier /** - * A class for naming Jupiter test methods in a compatible manner, - * taking into account several limitations imposed by the - * Android instrumentation (e.g. on isolated test runs). + * A class for naming Jupiter test methods in a compatible manner, taking into account several + * limitations imposed by the Android instrumentation (e.g. on isolated test runs). */ internal object TestNameFormatter { fun format(identifier: TestIdentifier, legacyFormat: Boolean = false): String { // When requesting the legacy format of the formatter, // construct a technical version of its name for backwards compatibility - // with the JUnit 4-based instrumentation of Android by stripping the brackets of parameterized tests completely. + // with the JUnit 4-based instrumentation of Android by stripping the brackets of + // parameterized tests completely. // If this didn't happen, running them from the IDE will cause "No tests found" errors. - // See AndroidX's TestRequestBuilder$MethodFilter for where this is cross-referenced in the instrumentation! + // See AndroidX's TestRequestBuilder$MethodFilter for where this is cross-referenced in the + // instrumentation! // // History: // - #199 & #207 (the original unearthing of this behavior) // - #317 (making an exception for dynamic tests) - // - #339 (retain indices of parameterized methods to avoid premature filtering by JUnit 4's test discovery) + // - #339 (retain indices of parameterized methods to avoid premature filtering by JUnit 4's + // test discovery) if (legacyFormat) { val reportName = identifier.legacyReportingName val paramStartIndex = reportName.indexOf('(') diff --git a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/AndroidJUnitFramework.kt b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/AndroidJUnitFramework.kt index be6aef8e..f82f8e26 100644 --- a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/AndroidJUnitFramework.kt +++ b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/AndroidJUnitFramework.kt @@ -11,9 +11,8 @@ import org.junit.runner.Runner import org.junit.runner.notification.RunNotifier /** - * JUnit Runner implementation using the JUnit Platform as its backbone. - * Serves as an intermediate solution to writing JUnit 5-based instrumentation tests - * until official support arrives for this. + * JUnit Runner implementation using the JUnit Platform as its backbone. Serves as an intermediate + * solution to writing JUnit 5-based instrumentation tests until official support arrives for this. */ @RequiresApi(26) @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) @@ -46,25 +45,27 @@ internal class AndroidJUnitFramework( if (isUsingOrchestrator && params.isParallelExecutionEnabled) { throw RuntimeException( """ - Running tests with the Android Test Orchestrator does not work with parallel tests, - since some information must be retained across parallel test execution, - and the isolated nature of the Android Test Orchestrator thwarts these efforts. - Please disable either setting and try again. - """.trimIndent(), + Running tests with the Android Test Orchestrator does not work with parallel tests, + since some information must be retained across parallel test execution, + and the isolated nature of the Android Test Orchestrator thwarts these efforts. + Please disable either setting and try again. + """ + .trimIndent() ) } - val testPlan = try { - launcher.discover(request) - } catch (e: JUnitException) { - // Each class in scope is given to the runner, - // but some may fail to be loaded by the class loader - // (e.g. when they are tailored to JVM work and reference sun.* classes - // or anything else not present in the Android runtime). - // Log those to console, but discard them from being considered at all - e.printStackTrace() - EmptyTestPlan - } + val testPlan = + try { + launcher.discover(request) + } catch (e: JUnitException) { + // Each class in scope is given to the runner, + // but some may fail to be loaded by the class loader + // (e.g. when they are tailored to JVM work and reference sun.* classes + // or anything else not present in the Android runtime). + // Log those to console, but discard them from being considered at all + e.printStackTrace() + EmptyTestPlan + } return AndroidJUnitPlatformTestTree( testPlan = testPlan, diff --git a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/AndroidJUnitPlatformRunnerListener.kt b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/AndroidJUnitPlatformRunnerListener.kt index 038bee42..0295effb 100644 --- a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/AndroidJUnitPlatformRunnerListener.kt +++ b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/AndroidJUnitPlatformRunnerListener.kt @@ -13,13 +13,11 @@ import org.junit.runner.Description import org.junit.runner.notification.Failure import org.junit.runner.notification.RunNotifier -/** - * Required, public extension to allow access to package-private RunnerListener class - */ +/** Required, public extension to allow access to package-private RunnerListener class */ @SuppressLint("NewApi") internal class AndroidJUnitPlatformRunnerListener( private val testTree: AndroidJUnitPlatformTestTree, - private val notifier: RunNotifier + private val notifier: RunNotifier, ) : TestExecutionListener { override fun reportingEntryPublished(testIdentifier: TestIdentifier, entry: ReportEntry) { @@ -45,12 +43,13 @@ internal class AndroidJUnitPlatformRunnerListener( when { testIdentifier.isTest -> fireTestIgnored(testIdentifier, reason) testIdentifier.isDynamicTest -> fireTestIgnored(testIdentifier, reason) - testIdentifier.isContainer -> testTree.getChildren(testIdentifier).forEach { childIdentifier -> - // Only report leaf tests as skipped - if (childIdentifier.isTest || childIdentifier.isDynamicTest) { - fireTestIgnored(childIdentifier, reason) + testIdentifier.isContainer -> + testTree.getChildren(testIdentifier).forEach { childIdentifier -> + // Only report leaf tests as skipped + if (childIdentifier.isTest || childIdentifier.isDynamicTest) { + fireTestIgnored(childIdentifier, reason) + } } - } } } @@ -62,7 +61,7 @@ internal class AndroidJUnitPlatformRunnerListener( override fun executionFinished( testIdentifier: TestIdentifier, - testExecutionResult: TestExecutionResult + testExecutionResult: TestExecutionResult, ) { val description = testTree.getDescription(testIdentifier) val status = testExecutionResult.status @@ -84,6 +83,6 @@ internal class AndroidJUnitPlatformRunnerListener( private fun toFailure( testExecutionResult: TestExecutionResult, - description: Description + description: Description, ): Failure = Failure(description, testExecutionResult.throwable.orElse(null)) -} \ No newline at end of file +} diff --git a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/AndroidJUnitPlatformTestTree.kt b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/AndroidJUnitPlatformTestTree.kt index 9b7c703e..14a7dfae 100644 --- a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/AndroidJUnitPlatformTestTree.kt +++ b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/AndroidJUnitPlatformTestTree.kt @@ -5,6 +5,8 @@ package de.mannodermaus.junit5.internal.runners import android.annotation.SuppressLint import de.mannodermaus.junit5.internal.extensions.format import de.mannodermaus.junit5.internal.extensions.isDynamicTest +import java.util.Optional +import java.util.function.Predicate import org.junit.platform.commons.util.AnnotationUtils import org.junit.platform.engine.UniqueId import org.junit.platform.engine.support.descriptor.ClassSource @@ -13,13 +15,11 @@ import org.junit.platform.launcher.TestIdentifier import org.junit.platform.launcher.TestPlan import org.junit.platform.suite.api.SuiteDisplayName import org.junit.runner.Description -import java.util.Optional -import java.util.function.Predicate /** - * Required, public extension to allow access to package-private TestTree class. - * Furthermore, manipulate the test tree in a way that will fold dynamic tests - * into the test report, without having the Android instrumentation mess up their naming. + * Required, public extension to allow access to package-private TestTree class. Furthermore, + * manipulate the test tree in a way that will fold dynamic tests into the test report, without + * having the Android instrumentation mess up their naming. */ @SuppressLint("NewApi") internal class AndroidJUnitPlatformTestTree( @@ -37,39 +37,41 @@ internal class AndroidJUnitPlatformTestTree( // Order matters here, since all dynamic tests are also containers, // but not all containers are dynamic tests - fun getTestName(identifier: TestIdentifier): String = when { - identifier.isDynamicTest -> if (needLegacyFormat) { - // In isolated method runs, there is no need to compose - // dynamic test names from multiple pieces, as the - // Android Instrumentation only looks at the raw method name - // anyway and all information about parameter types is lost - identifier.format(true) - } else { - // Collect all dynamic tests' IDs from this identifier, - // all the way up to the first non-dynamic test. - // Collect the name of all these into a list, then finally - // compose the final name from this list. Note that, because we - // move upwards the test plan, the elements must be reversed - // before the final name can be composed. - val nameComponents = mutableListOf() - var currentNode: TestIdentifier? = identifier - while (currentNode != null && currentNode.isDynamicTest) { - nameComponents.add(currentNode.format(false)) - currentNode = modifiedTestPlan.getRealParent(currentNode).orElse(null) - } - nameComponents.reverse() - - // Android's Unified Test Platform (AGP 7.0+) is using literal test names - // to create files when capturing Logcat output during execution. - // Ergo, make sure that only legal characters are being used in the test names - // (ref. https://github.com/mannodermaus/android-junit5/issues/263) - nameComponents.joinToString(" - ") - } + fun getTestName(identifier: TestIdentifier): String = + when { + identifier.isDynamicTest -> + if (needLegacyFormat) { + // In isolated method runs, there is no need to compose + // dynamic test names from multiple pieces, as the + // Android Instrumentation only looks at the raw method name + // anyway and all information about parameter types is lost + identifier.format(true) + } else { + // Collect all dynamic tests' IDs from this identifier, + // all the way up to the first non-dynamic test. + // Collect the name of all these into a list, then finally + // compose the final name from this list. Note that, because we + // move upwards the test plan, the elements must be reversed + // before the final name can be composed. + val nameComponents = mutableListOf() + var currentNode: TestIdentifier? = identifier + while (currentNode != null && currentNode.isDynamicTest) { + nameComponents.add(currentNode.format(false)) + currentNode = modifiedTestPlan.getRealParent(currentNode).orElse(null) + } + nameComponents.reverse() + + // Android's Unified Test Platform (AGP 7.0+) is using literal test names + // to create files when capturing Logcat output during execution. + // Ergo, make sure that only legal characters are being used in the test names + // (ref. https://github.com/mannodermaus/android-junit5/issues/263) + nameComponents.joinToString(" - ") + } - identifier.isContainer -> getTechnicalName(identifier) + identifier.isContainer -> getTechnicalName(identifier) - else -> identifier.format(needLegacyFormat) - } + else -> identifier.format(needLegacyFormat) + } // Do not expose our custom TestPlan, because JUnit Platform wouldn't like that very much. // Only internally, use the wrapped version @@ -94,11 +96,7 @@ internal class AndroidJUnitPlatformTestTree( private fun buildDescriptionTree(suiteDescription: Description, testPlan: TestPlan) { testPlan.roots.forEach { identifier -> - buildDescription( - identifier, - suiteDescription, - testPlan - ) + buildDescription(identifier, suiteDescription, testPlan) } } @@ -110,18 +108,18 @@ internal class AndroidJUnitPlatformTestTree( private fun buildDescription( identifier: TestIdentifier, parent: Description, - testPlan: TestPlan + testPlan: TestPlan, ) { - val newDescription = createJUnit4Description(identifier, testPlan).also { - descriptions[identifier] = it - } + val newDescription = + createJUnit4Description(identifier, testPlan).also { descriptions[identifier] = it } - val newParent = if (identifier.isTest || identifier.isDynamicTest) { - parent.addChild(newDescription) - newDescription - } else { - parent - } + val newParent = + if (identifier.isTest || identifier.isDynamicTest) { + parent.addChild(newDescription) + newDescription + } else { + parent + } testPlan.getChildren(identifier).forEach { child -> buildDescription(child, newParent, testPlan) @@ -130,15 +128,14 @@ internal class AndroidJUnitPlatformTestTree( private fun createJUnit4Description( identifier: TestIdentifier, - testPlan: TestPlan + testPlan: TestPlan, ): Description { val name = nameExtractor(identifier) - return if (identifier.isTest || identifier.isDynamicTest) { Description.createTestDescription( - /* className = */ - testPlan.getParent(identifier) + /* className = */ testPlan + .getParent(identifier) .map(nameExtractor) .orElse(""), /* name = */ name, @@ -158,7 +155,6 @@ internal class AndroidJUnitPlatformTestTree( if (source is ClassSource) { return source.javaClass.name - } else if (source is MethodSource) { val methodParameterTypes = source.methodParameterTypes.orEmpty() return if (methodParameterTypes.isBlank()) { @@ -177,14 +173,13 @@ internal class AndroidJUnitPlatformTestTree( return modifiedTestPlan.getDescendants(testIdentifier) } - /** - * Custom drop-in TestPlan for Android purposes. - */ + /** Custom drop-in TestPlan for Android purposes. */ private class ModifiedTestPlan(delegate: TestPlan) : TestPlanAdapter(delegate) { fun getRealParent(child: TestIdentifier): Optional { // Because the overridden "getParent()" from the superclass is modified, - // expose this additional method to access the actual parent identifier of the given child. + // expose this additional method to access the actual parent identifier of the given + // child. // This is needed when composing the display name of a dynamic test. return delegate.getParent(child) } @@ -201,7 +196,9 @@ internal class AndroidJUnitPlatformTestTree( } } - private fun findEligibleParentOfDynamicTest(child: TestIdentifier): Optional { + private fun findEligibleParentOfDynamicTest( + child: TestIdentifier + ): Optional { var node = delegate.getParent(child) while (node.isPresent && node.get().isDynamicTest) { node = node.flatMap(delegate::getParent) @@ -248,4 +245,4 @@ internal class AndroidJUnitPlatformTestTree( return delegate.containsTests() } } -} \ No newline at end of file +} diff --git a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/DummyJUnitFramework.kt b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/DummyJUnitFramework.kt index c1261dee..db7a3706 100644 --- a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/DummyJUnitFramework.kt +++ b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/DummyJUnitFramework.kt @@ -5,14 +5,14 @@ import android.util.Log import de.mannodermaus.junit5.internal.JUNIT_FRAMEWORK_MINIMUM_SDK_VERSION import de.mannodermaus.junit5.internal.LOG_TAG import de.mannodermaus.junit5.internal.dummy.JupiterTestMethodFinder +import java.lang.reflect.Method import org.junit.runner.Description import org.junit.runner.Runner import org.junit.runner.notification.RunNotifier -import java.lang.reflect.Method /** - * Fake Runner that marks all JUnit Framework methods as ignored, - * used for old devices without the required Java capabilities. + * Fake Runner that marks all JUnit Framework methods as ignored, used for old devices without the + * required Java capabilities. */ internal class DummyJUnitFramework(private val testClass: Class<*>) : Runner() { @@ -22,9 +22,9 @@ internal class DummyJUnitFramework(private val testClass: Class<*>) : Runner() { Log.w( LOG_TAG, "JUnit Framework is not supported on this device: " + - "API level ${Build.VERSION.SDK_INT} is less than " + - "${JUNIT_FRAMEWORK_MINIMUM_SDK_VERSION}, the minimum requirement. " + - "All Jupiter tests for ${testClass.name} will be disabled." + "API level ${Build.VERSION.SDK_INT} is less than " + + "${JUNIT_FRAMEWORK_MINIMUM_SDK_VERSION}, the minimum requirement. " + + "All Jupiter tests for ${testClass.name} will be disabled.", ) for (testMethod in testMethods) { diff --git a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/JUnitFrameworkRunnerFactory.kt b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/JUnitFrameworkRunnerFactory.kt index 2dd7067f..779b0458 100644 --- a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/JUnitFrameworkRunnerFactory.kt +++ b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/JUnitFrameworkRunnerFactory.kt @@ -7,19 +7,20 @@ import org.junit.runner.Runner /** * Since we can't reference [AndroidJUnitFramework] directly, use this factory for instantiation. * - * On devices with sufficient API levels, delegate to the real implementation to drive - * the execution of JUnit Framework tests. Below this threshold, they wouldn't work, however; - * for this case, delegate to a dummy runner which will highlight these tests as ignored. + * On devices with sufficient API levels, delegate to the real implementation to drive the execution + * of JUnit Framework tests. Below this threshold, they wouldn't work, however; for this case, + * delegate to a dummy runner which will highlight these tests as ignored. */ internal fun tryCreateJUnitFrameworkRunner( klass: Class<*>, - paramsSupplier: () -> JUnitFrameworkRunnerParams + paramsSupplier: () -> JUnitFrameworkRunnerParams, ): Runner? { - val runner = if (Build.VERSION.SDK_INT >= JUNIT_FRAMEWORK_MINIMUM_SDK_VERSION) { - AndroidJUnitFramework(klass, paramsSupplier) - } else { - DummyJUnitFramework(klass) - } + val runner = + if (Build.VERSION.SDK_INT >= JUNIT_FRAMEWORK_MINIMUM_SDK_VERSION) { + AndroidJUnitFramework(klass, paramsSupplier) + } else { + DummyJUnitFramework(klass) + } // It's still possible for the runner to not be relevant to the test run, // which is related to how further filters are applied (e.g. via @Tag). @@ -29,5 +30,4 @@ internal fun tryCreateJUnitFrameworkRunner( return runner.takeIf(Runner::hasExecutableTests) } -private fun Runner.hasExecutableTests() = - this.description.children.isNotEmpty() +private fun Runner.hasExecutableTests() = this.description.children.isNotEmpty() diff --git a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/JUnitFrameworkRunnerParams.kt b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/JUnitFrameworkRunnerParams.kt index 0d9cfb7d..693634a9 100644 --- a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/JUnitFrameworkRunnerParams.kt +++ b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/JUnitFrameworkRunnerParams.kt @@ -16,7 +16,7 @@ internal data class JUnitFrameworkRunnerParams( private val filters: List> = emptyList(), val environmentVariables: Map = emptyMap(), val systemProperties: Map = emptyMap(), - private val configurationParameters: Map = emptyMap() + private val configurationParameters: Map = emptyMap(), ) { fun createSelectors(testClass: Class<*>): List { return ParsedSelectors.fromBundle(testClass, arguments) @@ -42,25 +42,30 @@ internal data class JUnitFrameworkRunnerParams( val arguments = InstrumentationRegistry.getArguments() // Parse environment variables & pass them to the JVM - val environmentVariables = arguments.getString(ARG_ENVIRONMENT_VARIABLES) - ?.run { PropertiesParser.fromString(this) } - ?: emptyMap() + val environmentVariables = + arguments.getString(ARG_ENVIRONMENT_VARIABLES)?.run { + PropertiesParser.fromString(this) + } ?: emptyMap() // Parse system properties & pass them to the JVM - val systemProperties = arguments.getString(ARG_SYSTEM_PROPERTIES) - ?.run { PropertiesParser.fromString(this) } - ?: emptyMap() + val systemProperties = + arguments.getString(ARG_SYSTEM_PROPERTIES)?.run { + PropertiesParser.fromString(this) + } ?: emptyMap() // Parse configuration parameters - val configurationParameters = arguments.getString(ARG_CONFIGURATION_PARAMETERS) - ?.run { PropertiesParser.fromString(this) } - ?: emptyMap() + val configurationParameters = + arguments.getString(ARG_CONFIGURATION_PARAMETERS)?.run { + PropertiesParser.fromString(this) + } ?: emptyMap() - // The user may apply test filters to their instrumentation tests through the Gradle plugin's DSL, + // The user may apply test filters to their instrumentation tests through the Gradle + // plugin's DSL, // which aren't subject to the filtering imposed through adb. // A special resource file may be looked up at runtime, containing // the filters to apply by the AndroidJUnit5 runner. - val filters = GeneratedFilters.fromContext(instrumentation.context) + + val filters = + GeneratedFilters.fromContext(instrumentation.context) + listOfNotNull(ShardingFilter.fromArguments(arguments)) return JUnitFrameworkRunnerParams( @@ -68,7 +73,7 @@ internal data class JUnitFrameworkRunnerParams( filters, environmentVariables, systemProperties, - configurationParameters + configurationParameters, ) } } diff --git a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/notification/FilteredRunListener.kt b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/notification/FilteredRunListener.kt index 3288c74a..6cf4ab4e 100644 --- a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/notification/FilteredRunListener.kt +++ b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/notification/FilteredRunListener.kt @@ -5,9 +5,9 @@ import org.junit.runner.notification.Failure import org.junit.runner.notification.RunListener /** - * A wrapper implementation around JUnit's [RunListener] class - * which only works selectively. In other words, this implementation only delegates - * to its parameter for test descriptors that pass the given [filter]. + * A wrapper implementation around JUnit's [RunListener] class which only works selectively. In + * other words, this implementation only delegates to its parameter for test descriptors that pass + * the given [filter]. */ internal class FilteredRunListener( private val delegate: RunListener, diff --git a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/notification/ParallelRunNotifier.kt b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/notification/ParallelRunNotifier.kt index 4229a76c..b0292a09 100644 --- a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/notification/ParallelRunNotifier.kt +++ b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/notification/ParallelRunNotifier.kt @@ -8,26 +8,25 @@ import android.util.Log import androidx.test.internal.runner.listener.InstrumentationResultPrinter import de.mannodermaus.junit5.internal.LOG_TAG import de.mannodermaus.junit5.internal.runners.notification.ParallelRunNotifier.EventThread.Event -import org.junit.runner.Description -import org.junit.runner.notification.Failure -import org.junit.runner.notification.RunListener -import org.junit.runner.notification.RunNotifier import java.util.concurrent.Executors import java.util.concurrent.LinkedBlockingDeque import java.util.concurrent.LinkedBlockingQueue import java.util.concurrent.TimeUnit +import org.junit.runner.Description +import org.junit.runner.notification.Failure +import org.junit.runner.notification.RunListener +import org.junit.runner.notification.RunNotifier /** - * Wrapping implementation of JUnit 4's run notifier for parallel test execution - * (i.e. when "junit.jupiter.execution.parallel.enabled" is active during the run). - * It unpacks the singular 'instrumentation result printer' assigned by AndroidX - * and reroutes its notification mechanism. This allows parallel tests to still execute in parallel, - * but also allows their results to be reported back in the strictly sequential order required by the instrumentation. + * Wrapping implementation of JUnit 4's run notifier for parallel test execution (i.e. when + * "junit.jupiter.execution.parallel.enabled" is active during the run). It unpacks the singular + * 'instrumentation result printer' assigned by AndroidX and reroutes its notification mechanism. + * This allows parallel tests to still execute in parallel, but also allows their results to be + * reported back in the strictly sequential order required by the instrumentation. */ internal class ParallelRunNotifier(private val delegate: RunNotifier) : RunNotifier() { private companion object { - @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") - private val doneLock = Object() + @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") private val doneLock = Object() private val nopPrinter = InstrumentationResultPrinter() private val nopTestState = TestState("", Bundle(), 0) @@ -52,7 +51,8 @@ internal class ParallelRunNotifier(private val delegate: RunNotifier) : RunNotif private val states = mutableMapOf() // Even though parallelism is the name of the game under the hood for this RunNotifier, - // the nature of the Android Instrumentation is very much bound to synchronous execution internally. + // the nature of the Android Instrumentation is very much bound to synchronous execution + // internally. // Therefore, a single-threaded executor must be used to project the multithreaded notifications // from JUnit 5 onto this legacy thread model, resulting in some funky test reporting // but allowing the awesome performance benefits of parallel test execution! @@ -70,10 +70,9 @@ internal class ParallelRunNotifier(private val delegate: RunNotifier) : RunNotif delegate.fireTestSuiteStarted(description) // Start asynchronous processing pipeline - eventThread = EventThread( - onProcessEvent = ::onProcessEvent, - onDone = ::onDone, - ).also(EventThread::start) + eventThread = + EventThread(onProcessEvent = ::onProcessEvent, onDone = ::onDone) + .also(EventThread::start) } override fun fireTestStarted(description: Description) { @@ -112,54 +111,55 @@ internal class ParallelRunNotifier(private val delegate: RunNotifier) : RunNotif /* Private */ - private fun onProcessEvent(event: Event) = executor.submit { - val description = event.description - - when (event) { - is Event.Started -> { - delegate.fireTestStarted(description) - printer.testStarted(description) + private fun onProcessEvent(event: Event) = + executor.submit { + val description = event.description - // Persist the current printer state for this test - // (for later, when this test's finish event comes in) - states[description.uniqueIdentifier] = printer.captureTestState() - } - - is Event.Ignored -> { - delegate.fireTestIgnored(description) - printer.testIgnored(description) - } + when (event) { + is Event.Started -> { + delegate.fireTestStarted(description) + printer.testStarted(description) - is Event.Finished -> { - // Restore the printer state to the current test case, - // then fire the relevant lifecycle methods of the delegate notifier - printer.restoreTestState(description) - - // For failed test cases, always invoke the failure methods first, - // but invoke the 'finished' method pair for all cases - when { - event.testFailure != null -> { - delegate.fireTestFailure(event.testFailure) - printer.testFailure(event.testFailure) - delegate.fireTestFinished(description) - printer.testFinished(description) - } + // Persist the current printer state for this test + // (for later, when this test's finish event comes in) + states[description.uniqueIdentifier] = printer.captureTestState() + } - event.assumptionFailure != null -> { - delegate.fireTestAssumptionFailed(event.assumptionFailure) - printer.testAssumptionFailure(event.assumptionFailure) - delegate.fireTestFinished(description) - printer.testFinished(description) - } + is Event.Ignored -> { + delegate.fireTestIgnored(description) + printer.testIgnored(description) + } - else -> { - delegate.fireTestFinished(description) - printer.testFinished(description) + is Event.Finished -> { + // Restore the printer state to the current test case, + // then fire the relevant lifecycle methods of the delegate notifier + printer.restoreTestState(description) + + // For failed test cases, always invoke the failure methods first, + // but invoke the 'finished' method pair for all cases + when { + event.testFailure != null -> { + delegate.fireTestFailure(event.testFailure) + printer.testFailure(event.testFailure) + delegate.fireTestFinished(description) + printer.testFinished(description) + } + + event.assumptionFailure != null -> { + delegate.fireTestAssumptionFailed(event.assumptionFailure) + printer.testAssumptionFailure(event.assumptionFailure) + delegate.fireTestFinished(description) + printer.testFinished(description) + } + + else -> { + delegate.fireTestFinished(description) + printer.testFinished(description) + } } } } } - } private fun onDone(description: Description?) { synchronized(doneLock) { @@ -191,8 +191,7 @@ internal class ParallelRunNotifier(private val delegate: RunNotifier) : RunNotif } private val Description.uniqueIdentifier - get() = - "$className-$displayName" + get() = "$className-$displayName" private class EventThread( private val onProcessEvent: (Event) -> Unit, @@ -202,6 +201,7 @@ internal class ParallelRunNotifier(private val delegate: RunNotifier) : RunNotif val description: Description data class Started(override val description: Description) : Event + data class Finished( override val description: Description, val testFailure: Failure? = null, @@ -285,21 +285,29 @@ internal class ParallelRunNotifier(private val delegate: RunNotifier) : RunNotif @Suppress("UNCHECKED_CAST") private class Reflection { - private fun Class.field(name: String) = this.getDeclaredField(name).also { it.isAccessible = true } + private fun Class.field(name: String) = + this.getDeclaredField(name).also { it.isAccessible = true } - private val synchronizedRunListenerClass = Class.forName("org.junit.runner.notification.SynchronizedRunListener") - private val synchronizedListenerDelegateField = synchronizedRunListenerClass.field("listener") + private val synchronizedRunListenerClass = + Class.forName("org.junit.runner.notification.SynchronizedRunListener") + private val synchronizedListenerDelegateField = + synchronizedRunListenerClass.field("listener") private val runNotifierListenersField = RunNotifier::class.java.field("listeners") - private val resultPrinterTestResultField = InstrumentationResultPrinter::class.java.field("testResult") - private val resultPrinterTestResultCodeField = InstrumentationResultPrinter::class.java.field("testResultCode") - private val resultPrinterTestClassField = InstrumentationResultPrinter::class.java.field("testClass") + private val resultPrinterTestResultField = + InstrumentationResultPrinter::class.java.field("testResult") + private val resultPrinterTestResultCodeField = + InstrumentationResultPrinter::class.java.field("testResultCode") + private val resultPrinterTestClassField = + InstrumentationResultPrinter::class.java.field("testClass") private var cached: InstrumentationResultPrinter? = null fun initialize(notifier: RunNotifier): InstrumentationResultPrinter? { try { // The printer needs to be retrieved only once per test run - cached?.let { return it } + cached?.let { + return it + } // The Android system registers a global listener // for communicating status events back to the instrumentation. @@ -311,9 +319,10 @@ internal class ParallelRunNotifier(private val delegate: RunNotifier) : RunNotif // The Android instrumentation may wrap the printer inside another JUnit listener, // so make sure to search for the result inside its toString() representation // (rather than through an 'it is X' check) - val candidate = listeners?.firstOrNull { - InstrumentationResultPrinter::class.java.name in it.toString() - } + val candidate = + listeners?.firstOrNull { + InstrumentationResultPrinter::class.java.name in it.toString() + } if (candidate != null) { // Replace the original listener with a wrapped version of itself, @@ -326,11 +335,13 @@ internal class ParallelRunNotifier(private val delegate: RunNotifier) : RunNotif // The Android instrumentation may wrap the printer inside another JUnit listener, // so make sure to search for the result inside its toString() representation // (rather than through an 'it is X' check) - val result = if (synchronizedRunListenerClass.isInstance(candidate)) { - synchronizedListenerDelegateField.get(candidate) as? InstrumentationResultPrinter - } else { - candidate as? InstrumentationResultPrinter - } + val result = + if (synchronizedRunListenerClass.isInstance(candidate)) { + synchronizedListenerDelegateField.get(candidate) + as? InstrumentationResultPrinter + } else { + candidate as? InstrumentationResultPrinter + } cached = result return result diff --git a/instrumentation/runner/src/six/kotlin/de/mannodermaus/junit5/internal/RunnerConstants.kt b/instrumentation/runner/src/six/kotlin/de/mannodermaus/junit5/internal/RunnerConstants.kt index 8ef01d5c..b6a86743 100644 --- a/instrumentation/runner/src/six/kotlin/de/mannodermaus/junit5/internal/RunnerConstants.kt +++ b/instrumentation/runner/src/six/kotlin/de/mannodermaus/junit5/internal/RunnerConstants.kt @@ -1,7 +1,7 @@ package de.mannodermaus.junit5.internal /** - * The minimum Android API level on which JUnit Framework tests may be executed. - * Trying to launch a test on an older device will simply mark it as 'skipped'. + * The minimum Android API level on which JUnit Framework tests may be executed. Trying to launch a + * test on an older device will simply mark it as 'skipped'. */ internal const val JUNIT_FRAMEWORK_MINIMUM_SDK_VERSION: Int = 35 diff --git a/instrumentation/runner/src/six/kotlin/de/mannodermaus/junit5/internal/discovery/EmptyConfigurationParameters.kt b/instrumentation/runner/src/six/kotlin/de/mannodermaus/junit5/internal/discovery/EmptyConfigurationParameters.kt index db7dbf3b..407fc5e8 100644 --- a/instrumentation/runner/src/six/kotlin/de/mannodermaus/junit5/internal/discovery/EmptyConfigurationParameters.kt +++ b/instrumentation/runner/src/six/kotlin/de/mannodermaus/junit5/internal/discovery/EmptyConfigurationParameters.kt @@ -1,15 +1,15 @@ package de.mannodermaus.junit5.internal.discovery import androidx.annotation.RequiresApi -import org.junit.platform.engine.ConfigurationParameters import java.util.Optional +import org.junit.platform.engine.ConfigurationParameters -/** - * JUnit 6 version of the [ConfigurationParameters] interface. - */ +/** JUnit 6 version of the [ConfigurationParameters] interface. */ @RequiresApi(26) internal object EmptyConfigurationParameters : ConfigurationParameters { override fun get(key: String) = Optional.empty() + override fun getBoolean(key: String) = Optional.empty() + override fun keySet() = emptySet() } diff --git a/instrumentation/runner/src/six/kotlin/de/mannodermaus/junit5/internal/runners/TestPlanAdapter.kt b/instrumentation/runner/src/six/kotlin/de/mannodermaus/junit5/internal/runners/TestPlanAdapter.kt index 837778a8..842c24bc 100644 --- a/instrumentation/runner/src/six/kotlin/de/mannodermaus/junit5/internal/runners/TestPlanAdapter.kt +++ b/instrumentation/runner/src/six/kotlin/de/mannodermaus/junit5/internal/runners/TestPlanAdapter.kt @@ -2,13 +2,10 @@ package de.mannodermaus.junit5.internal.runners import org.junit.platform.launcher.TestPlan -/** - * JUnit 6 version of the [TestPlanAdapter]. - */ -internal open class TestPlanAdapter( - val delegate: TestPlan -) : TestPlan( - /* containsTests = */ delegate.containsTests(), - /* configurationParameters = */ delegate.configurationParameters, - /* outputDirectoryCreator = */ delegate.outputDirectoryCreator -) +/** JUnit 6 version of the [TestPlanAdapter]. */ +internal open class TestPlanAdapter(val delegate: TestPlan) : + TestPlan( + /* containsTests = */ delegate.containsTests(), + /* configurationParameters = */ delegate.configurationParameters, + /* outputDirectoryCreator = */ delegate.outputDirectoryCreator, + ) diff --git a/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/AndroidJUnitFrameworkBuilderTests.kt b/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/AndroidJUnitFrameworkBuilderTests.kt index 6fc73aa0..46a4d583 100644 --- a/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/AndroidJUnitFrameworkBuilderTests.kt +++ b/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/AndroidJUnitFrameworkBuilderTests.kt @@ -14,35 +14,38 @@ class AndroidJUnitFrameworkBuilderTests { private val builder = AndroidJUnitFrameworkBuilder() @TestFactory - fun `no runner is created if class only contains top-level test methods`() = runTest( - expectSuccess = false, - // In Kotlin, a 'Kt'-suffixed class of top-level functions cannot be referenced - // via the ::class syntax, so construct a reference to the class directly - Class.forName(javaClass.packageName + ".TestClassesKt") - ) + fun `no runner is created if class only contains top-level test methods`() = + runTest( + expectSuccess = false, + // In Kotlin, a 'Kt'-suffixed class of top-level functions cannot be referenced + // via the ::class syntax, so construct a reference to the class directly + Class.forName(javaClass.packageName + ".TestClassesKt"), + ) @TestFactory - fun `runner is created correctly for classes with valid jupiter test methods`() = runTest( - expectSuccess = true, - HasTest::class.java, - HasRepeatedTest::class.java, - HasTestFactory::class.java, - HasTestTemplate::class.java, - HasParameterizedTest::class.java, - HasInnerClassWithTest::class.java, - HasTaggedTest::class.java, - HasInheritedTestsFromClass::class.java, - HasInheritedTestsFromInterface::class.java, - HasMultipleInheritancesAndOverrides::class.java, - ) + fun `runner is created correctly for classes with valid jupiter test methods`() = + runTest( + expectSuccess = true, + HasTest::class.java, + HasRepeatedTest::class.java, + HasTestFactory::class.java, + HasTestTemplate::class.java, + HasParameterizedTest::class.java, + HasInnerClassWithTest::class.java, + HasTaggedTest::class.java, + HasInheritedTestsFromClass::class.java, + HasInheritedTestsFromInterface::class.java, + HasMultipleInheritancesAndOverrides::class.java, + ) @TestFactory - fun `no runner is created if class has no jupiter test methods`() = runTest( - expectSuccess = false, - DoesntHaveTestMethods::class.java, - HasJUnit4Tests::class.java, - kotlin.time.Duration::class.java, - ) + fun `no runner is created if class has no jupiter test methods`() = + runTest( + expectSuccess = false, + DoesntHaveTestMethods::class.java, + HasJUnit4Tests::class.java, + kotlin.time.Duration::class.java, + ) /* Private */ @@ -52,20 +55,21 @@ class AndroidJUnitFrameworkBuilderTests { return classes.map { cls -> dynamicContainer( /* displayName = */ cls.name, - /* dynamicNodes = */ setOf(Build.VERSION_CODES.M, Build.VERSION_CODES.TIRAMISU).map { apiLevel -> - dynamicTest("API Level $apiLevel") { - withMockedInstrumentation { - withApiLevel(apiLevel) { - val runner = builder.runnerForClass(cls) - if (expectSuccess) { - assertThat(runner).isNotNull() - } else { - assertThat(runner).isNull() + /* dynamicNodes = */ setOf(Build.VERSION_CODES.M, Build.VERSION_CODES.TIRAMISU) + .map { apiLevel -> + dynamicTest("API Level $apiLevel") { + withMockedInstrumentation { + withApiLevel(apiLevel) { + val runner = builder.runnerForClass(cls) + if (expectSuccess) { + assertThat(runner).isNotNull() + } else { + assertThat(runner).isNull() + } } } } - } - } + }, ) } } diff --git a/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/TestClasses.kt b/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/TestClasses.kt index 3e75ab2d..d3cff471 100644 --- a/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/TestClasses.kt +++ b/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/TestClasses.kt @@ -1,118 +1,93 @@ package de.mannodermaus.junit5 +import java.util.stream.Stream import org.junit.jupiter.api.* import org.junit.jupiter.api.DynamicTest.dynamicTest import org.junit.jupiter.api.extension.* import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.CsvSource import org.junit.jupiter.params.provider.ValueSource -import java.util.stream.Stream class DoesntHaveTestMethods class HasTest { - @Test - fun method() { - } + @Test fun method() {} } class HasRepeatedTest { - @RepeatedTest(5) - fun method(info: RepetitionInfo) { - } + @RepeatedTest(5) fun method(info: RepetitionInfo) {} } class HasTestFactory { - @TestFactory - fun method() = listOf( - dynamicTest("a") {}, - dynamicTest("b") {} - ) + @TestFactory fun method() = listOf(dynamicTest("a") {}, dynamicTest("b") {}) } class HasTestTemplate { - @TestTemplate - @ExtendWith(ExampleInvocationContextProvider::class) - fun method(param: String) { - } - - class ExampleInvocationContextProvider : TestTemplateInvocationContextProvider { - override fun provideTestTemplateInvocationContexts(context: ExtensionContext): Stream = - listOf("param1", "param2") - .map(this::context) - .stream() - - override fun supportsTestTemplate(context: ExtensionContext) = true - - private fun context(param: String): TestTemplateInvocationContext = - object : TestTemplateInvocationContext { - override fun getDisplayName(invocationIndex: Int): String = param - - override fun getAdditionalExtensions() = listOf( - object : ParameterResolver { - override fun supportsParameter(parameterContext: ParameterContext, extensionContext: ExtensionContext) = - parameterContext.parameter.type == String::class.java - - override fun resolveParameter(parameterContext: ParameterContext, extensionContext: ExtensionContext) = - param - } - ) - } - } + @TestTemplate @ExtendWith(ExampleInvocationContextProvider::class) fun method(param: String) {} + + class ExampleInvocationContextProvider : TestTemplateInvocationContextProvider { + override fun provideTestTemplateInvocationContexts( + context: ExtensionContext + ): Stream = + listOf("param1", "param2").map(this::context).stream() + + override fun supportsTestTemplate(context: ExtensionContext) = true + + private fun context(param: String): TestTemplateInvocationContext = + object : TestTemplateInvocationContext { + override fun getDisplayName(invocationIndex: Int): String = param + + override fun getAdditionalExtensions() = + listOf( + object : ParameterResolver { + override fun supportsParameter( + parameterContext: ParameterContext, + extensionContext: ExtensionContext, + ) = parameterContext.parameter.type == String::class.java + + override fun resolveParameter( + parameterContext: ParameterContext, + extensionContext: ExtensionContext, + ) = param + } + ) + } + } } class HasParameterizedTest { - @ParameterizedTest - @CsvSource("a", "b") - fun method(param: String) { - } + @ParameterizedTest @CsvSource("a", "b") fun method(param: String) {} } class HasInnerClassWithTest { - @Nested - inner class InnerClass { - @Test - fun method() { + @Nested + inner class InnerClass { + @Test fun method() {} } - } } class HasTaggedTest { - @Tag("slow") - @Test - fun method() { - - } + @Tag("slow") @Test fun method() {} } abstract class AbstractTestClass { - @Test - open fun abstractTest() { - } + @Test open fun abstractTest() {} } interface AbstractTestInterface { - @Test - fun interfaceTest() { - } + @Test fun interfaceTest() {} } class HasInheritedTestsFromClass : AbstractTestClass() { - @Test - fun method() { - } + @Test fun method() {} } class HasInheritedTestsFromInterface : AbstractTestInterface class HasMultipleInheritancesAndOverrides : AbstractTestClass(), AbstractTestInterface { - @Test - override fun abstractTest() { - } + @Test override fun abstractTest() {} - @Test - fun someOtherTest() { - } + @Test fun someOtherTest() {} } // These tests should not be acknowledged, @@ -120,19 +95,15 @@ class HasMultipleInheritancesAndOverrides : AbstractTestClass(), AbstractTestInt // are unsupported by JUnit 5 class HasJUnit4Tests { - @org.junit.Test - fun method() {} + @org.junit.Test fun method() {} } -@RepeatedTest(2) -fun topLevelRepeatedTest(unused: RepetitionInfo) {} +@RepeatedTest(2) fun topLevelRepeatedTest(unused: RepetitionInfo) {} @ValueSource(strings = ["a", "b"]) @ParameterizedTest fun topLevelParameterizedTest(unused: String) {} -@TestTemplate -fun topLevelTestTemplate() {} +@TestTemplate fun topLevelTestTemplate() {} -@TestFactory -fun topLevelTestFactory(): Stream = Stream.empty() +@TestFactory fun topLevelTestFactory(): Stream = Stream.empty() diff --git a/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/TestHelpers.kt b/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/TestHelpers.kt index 26be67cb..0abd7ee9 100644 --- a/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/TestHelpers.kt +++ b/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/TestHelpers.kt @@ -1,30 +1,31 @@ package de.mannodermaus.junit5 +import kotlin.reflect.KClass import org.junit.platform.engine.discovery.DiscoverySelectors import org.junit.platform.launcher.Launcher import org.junit.platform.launcher.TestPlan import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder import org.junit.platform.launcher.core.LauncherFactory -import kotlin.reflect.KClass /** - * A quick one-liner for executing a Jupiter discover-and-execute pass - * from inside of a Jupiter test. Useful for testing runner code - * that needs to work with the innards of the [TestPlan], such as - * individual test identifiers and such. + * A quick one-liner for executing a Jupiter discover-and-execute pass from inside of a Jupiter + * test. Useful for testing runner code that needs to work with the innards of the [TestPlan], such + * as individual test identifiers and such. */ fun discoverTests( cls: KClass<*>, launcher: Launcher = LauncherFactory.create(), executeAsWell: Boolean = true, ): TestPlan { - return launcher.discover( - LauncherDiscoveryRequestBuilder.request() - .selectors(DiscoverySelectors.selectClass(cls.java)) - .build() - ).also { plan -> - if (executeAsWell) { - launcher.execute(plan) + return launcher + .discover( + LauncherDiscoveryRequestBuilder.request() + .selectors(DiscoverySelectors.selectClass(cls.java)) + .build() + ) + .also { plan -> + if (executeAsWell) { + launcher.execute(plan) + } } - } } diff --git a/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/internal/discovery/PropertiesParserTests.kt b/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/internal/discovery/PropertiesParserTests.kt index 2c2970af..6aadba25 100644 --- a/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/internal/discovery/PropertiesParserTests.kt +++ b/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/internal/discovery/PropertiesParserTests.kt @@ -5,39 +5,31 @@ import org.junit.jupiter.api.Test class PropertiesParserTests { - @Test - fun `test valid string, containing one entry`() { - val string = "KEY1=true" - val variables = PropertiesParser.fromString(string) - assertThat(variables).containsExactly( - "KEY1", "true" - ) - } + @Test + fun `test valid string, containing one entry`() { + val string = "KEY1=true" + val variables = PropertiesParser.fromString(string) + assertThat(variables).containsExactly("KEY1", "true") + } - @Test - fun `test valid string, containing multiple entries`() { - val string = "KEY1=true,KEY2=123,KEY3=lol" - val variables = PropertiesParser.fromString(string) - assertThat(variables).containsExactly( - "KEY1", "true", - "KEY2", "123", - "KEY3", "lol" - ) - } + @Test + fun `test valid string, containing multiple entries`() { + val string = "KEY1=true,KEY2=123,KEY3=lol" + val variables = PropertiesParser.fromString(string) + assertThat(variables).containsExactly("KEY1", "true", "KEY2", "123", "KEY3", "lol") + } - @Test - fun `test invalid string, filter out those entries`() { - val string = "KEY1=true,INVALID1,INVALID2=lol=lolol,1234567" - val variables = PropertiesParser.fromString(string) - assertThat(variables).containsExactly( - "KEY1", "true" - ) - } + @Test + fun `test invalid string, filter out those entries`() { + val string = "KEY1=true,INVALID1,INVALID2=lol=lolol,1234567" + val variables = PropertiesParser.fromString(string) + assertThat(variables).containsExactly("KEY1", "true") + } - @Test - fun `test invalid string, return empty map`() { - val string = "" - val variables = PropertiesParser.fromString(string) - assertThat(variables).isEmpty() - } + @Test + fun `test invalid string, return empty map`() { + val string = "" + val variables = PropertiesParser.fromString(string) + assertThat(variables).isEmpty() + } } diff --git a/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/internal/dummy/JupiterTestMethodFinderTests.kt b/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/internal/dummy/JupiterTestMethodFinderTests.kt index b3d684e1..439b19dd 100644 --- a/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/internal/dummy/JupiterTestMethodFinderTests.kt +++ b/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/internal/dummy/JupiterTestMethodFinderTests.kt @@ -31,19 +31,20 @@ import org.junit.runner.notification.RunNotifier class JupiterTestMethodFinderTests { // Each element is a Pair of 'test class' and 'number of expected tests' - private val classes = listOf( - DoesntHaveTestMethods::class.java to 0, - HasTaggedTest::class.java to 1, - HasTest::class.java to 1, - HasRepeatedTest::class.java to 5, - HasTestFactory::class.java to 2, - HasTestTemplate::class.java to 2, - HasParameterizedTest::class.java to 2, - HasInnerClassWithTest::class.java to 1, - HasInheritedTestsFromClass::class.java to 2, - HasInheritedTestsFromInterface::class.java to 1, - HasMultipleInheritancesAndOverrides::class.java to 3, - ) + private val classes = + listOf( + DoesntHaveTestMethods::class.java to 0, + HasTaggedTest::class.java to 1, + HasTest::class.java to 1, + HasRepeatedTest::class.java to 5, + HasTestFactory::class.java to 2, + HasTestTemplate::class.java to 2, + HasParameterizedTest::class.java to 2, + HasInnerClassWithTest::class.java to 1, + HasInheritedTestsFromClass::class.java to 2, + HasInheritedTestsFromInterface::class.java to 1, + HasMultipleInheritancesAndOverrides::class.java to 3, + ) private val allFinders = setOf(JupiterTestMethodFinder) @@ -59,7 +60,9 @@ class JupiterTestMethodFinderTests { assertThat(methods).isNotEmpty() val result = runJUnit5(cls) - assertWithMessage("Executed ${result.count()} instead of $testCount tests on class '${cls.simpleName}': '${result.methodNames()}'") + assertWithMessage( + "Executed ${result.count()} instead of $testCount tests on class '${cls.simpleName}': '${result.methodNames()}'" + ) .that(result.count()) .isEqualTo(testCount) } @@ -69,12 +72,12 @@ class JupiterTestMethodFinderTests { @Test fun `check that tag filter works`() { - val result = runJUnit5( - cls = HasTaggedTest::class.java, - filter = TagFilter.excludeTags("slow"), - ) + val result = + runJUnit5(cls = HasTaggedTest::class.java, filter = TagFilter.excludeTags("slow")) - assertWithMessage("Executed ${result.count()} instead of 0 tests: '${result.methodNames()}'") + assertWithMessage( + "Executed ${result.count()} instead of 0 tests: '${result.methodNames()}'" + ) .that(result.count()) .isEqualTo(0) } @@ -83,17 +86,11 @@ class JupiterTestMethodFinderTests { private fun testForEachFinder(block: (JupiterTestMethodFinder) -> List) = allFinders.map { finder -> - dynamicContainer( - "using ${finder.javaClass.simpleName}", - block(finder), - ) + dynamicContainer("using ${finder.javaClass.simpleName}", block(finder)) } @CheckResult - private fun runJUnit5( - cls: Class<*>, - filter: Filter<*>? = null, - ): CountingRunListener { + private fun runJUnit5(cls: Class<*>, filter: Filter<*>? = null): CountingRunListener { // Verify number of executed test cases as well val notifier = RunNotifier() val listener = CountingRunListener() diff --git a/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/internal/formatters/TestNameFormatterTests.kt b/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/internal/formatters/TestNameFormatterTests.kt index 4ccdc08f..4beb2bc3 100644 --- a/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/internal/formatters/TestNameFormatterTests.kt +++ b/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/internal/formatters/TestNameFormatterTests.kt @@ -8,73 +8,76 @@ import de.mannodermaus.junit5.HasTestFactory import de.mannodermaus.junit5.HasTestTemplate import de.mannodermaus.junit5.discoverTests import de.mannodermaus.junit5.internal.extensions.format +import kotlin.reflect.KClass import org.junit.jupiter.api.Test -import org.junit.platform.engine.discovery.DiscoverySelectors import org.junit.platform.launcher.TestIdentifier import org.junit.platform.launcher.TestPlan -import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder -import org.junit.platform.launcher.core.LauncherFactory -import kotlin.reflect.KClass class TestNameFormatterTests { @Test - fun `normal junit5 test`() = runTestWith(HasTest::class) { identifier -> - assertThat(identifier.format(false)).isEqualTo("method") - assertThat(identifier.format(true)).isEqualTo("method") - } + fun `normal junit5 test`() = + runTestWith(HasTest::class) { identifier -> + assertThat(identifier.format(false)).isEqualTo("method") + assertThat(identifier.format(true)).isEqualTo("method") + } @Test - fun `repeated test`() = runTestWith(HasRepeatedTest::class) { identifier -> - assertThat(identifier.format(false)).isEqualTo("method[RepetitionInfo]") - assertThat(identifier.format(true)).isEqualTo("method") - - // Inspect individual executions, too - assertChildren(identifier, expectedCount = 5) { index, child -> - val number = index + 1 - assertThat(child.format(false)).isEqualTo("repetition $number of 5") - assertThat(child.format(true)).isEqualTo("method[$number]") + fun `repeated test`() = + runTestWith(HasRepeatedTest::class) { identifier -> + assertThat(identifier.format(false)).isEqualTo("method[RepetitionInfo]") + assertThat(identifier.format(true)).isEqualTo("method") + + // Inspect individual executions, too + assertChildren(identifier, expectedCount = 5) { index, child -> + val number = index + 1 + assertThat(child.format(false)).isEqualTo("repetition $number of 5") + assertThat(child.format(true)).isEqualTo("method[$number]") + } } - } @Test - fun `test factory`() = runTestWith(HasTestFactory::class) { identifier -> - assertThat(identifier.format(false)).isEqualTo("method") - assertThat(identifier.format(true)).isEqualTo("method") - - // Inspect individual executions, too - assertChildren(identifier, expectedCount = 2) { index, child -> - val number = index + 1 - assertThat(child.format(false)).isEqualTo(if (index == 0) "a" else "b") - assertThat(child.format(true)).isEqualTo("method[$number]") + fun `test factory`() = + runTestWith(HasTestFactory::class) { identifier -> + assertThat(identifier.format(false)).isEqualTo("method") + assertThat(identifier.format(true)).isEqualTo("method") + + // Inspect individual executions, too + assertChildren(identifier, expectedCount = 2) { index, child -> + val number = index + 1 + assertThat(child.format(false)).isEqualTo(if (index == 0) "a" else "b") + assertThat(child.format(true)).isEqualTo("method[$number]") + } } - } @Test - fun `test template`() = runTestWith(HasTestTemplate::class) { identifier -> - assertThat(identifier.format(false)).isEqualTo("method[String]") - assertThat(identifier.format(true)).isEqualTo("method") - - // Inspect individual executions, too - assertChildren(identifier, expectedCount = 2) { index, child -> - val number = index + 1 - assertThat(child.format(false)).isEqualTo("param$number") - assertThat(child.format(true)).isEqualTo("method[$number]") + fun `test template`() = + runTestWith(HasTestTemplate::class) { identifier -> + assertThat(identifier.format(false)).isEqualTo("method[String]") + assertThat(identifier.format(true)).isEqualTo("method") + + // Inspect individual executions, too + assertChildren(identifier, expectedCount = 2) { index, child -> + val number = index + 1 + assertThat(child.format(false)).isEqualTo("param$number") + assertThat(child.format(true)).isEqualTo("method[$number]") + } } - } @Test - fun `parameterized test`() = runTestWith(HasParameterizedTest::class) { identifier -> - assertThat(identifier.format(false)).isEqualTo("method[String]") - assertThat(identifier.format(true)).isEqualTo("method") - - // Inspect individual executions, too - assertChildren(identifier, expectedCount = 2) { index, child -> - val number = index + 1 - assertThat(child.format(false)).isEqualTo("[$number] " + if (index == 0) "a" else "b") - assertThat(child.format(true)).isEqualTo("method[$number]") + fun `parameterized test`() = + runTestWith(HasParameterizedTest::class) { identifier -> + assertThat(identifier.format(false)).isEqualTo("method[String]") + assertThat(identifier.format(true)).isEqualTo("method") + + // Inspect individual executions, too + assertChildren(identifier, expectedCount = 2) { index, child -> + val number = index + 1 + assertThat(child.format(false)) + .isEqualTo("[$number] " + if (index == 0) "a" else "b") + assertThat(child.format(true)).isEqualTo("method[$number]") + } } - } /* Private */ @@ -98,7 +101,7 @@ class TestNameFormatterTests { private fun TestPlan.assertChildren( of: TestIdentifier, expectedCount: Int, - block: (Int, TestIdentifier) -> Unit + block: (Int, TestIdentifier) -> Unit, ) { with(getChildren(of)) { assertThat(size).isEqualTo(expectedCount) diff --git a/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/internal/runners/AndroidJUnitFrameworkTests.kt b/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/internal/runners/AndroidJUnitFrameworkTests.kt index 6f571535..f9387287 100644 --- a/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/internal/runners/AndroidJUnitFrameworkTests.kt +++ b/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/internal/runners/AndroidJUnitFrameworkTests.kt @@ -4,6 +4,7 @@ import android.os.Bundle import com.google.common.truth.Truth.assertThat import de.mannodermaus.junit5.testutil.AndroidBuildUtils.withMockedInstrumentation import de.mannodermaus.junit5.testutil.CollectingRunListener +import java.util.concurrent.atomic.AtomicReference import org.junit.jupiter.api.DynamicContainer import org.junit.jupiter.api.DynamicContainer.dynamicContainer import org.junit.jupiter.api.DynamicTest.dynamicTest @@ -15,7 +16,6 @@ import org.junit.jupiter.params.provider.ValueSource import org.junit.runner.RunWith import org.junit.runner.notification.RunNotifier import org.robolectric.RobolectricTestRunner -import java.util.concurrent.atomic.AtomicReference @RunWith(RobolectricTestRunner::class) class AndroidJUnitFrameworkTests { @@ -25,16 +25,17 @@ class AndroidJUnitFrameworkTests { val results = runTests() val successNames = results.runTestNames - assertThat(successNames).containsExactly( - "normal test", - "testFactory - container - test 1", - "testFactory - container - test 2", - "repeatedTest - repetition 1 of 3", - "repeatedTest - repetition 2 of 3", - "repeatedTest - repetition 3 of 3", - "parameterizedTest(String) - [1] hello", - "parameterizedTest(String) - [2] world", - ) + assertThat(successNames) + .containsExactly( + "normal test", + "testFactory - container - test 1", + "testFactory - container - test 2", + "repeatedTest - repetition 1 of 3", + "repeatedTest - repetition 2 of 3", + "repeatedTest - repetition 3 of 3", + "parameterizedTest(String) - [1] hello", + "parameterizedTest(String) - [2] world", + ) } @org.junit.Test @@ -48,13 +49,15 @@ class AndroidJUnitFrameworkTests { val allResults = mutableListOf() for (i in 0..4) { - val results = runTests( - shardingConfig = if (i < 4) { - ShardingConfig(num = 4, index = i) - } else { - null - } - ) + val results = + runTests( + shardingConfig = + if (i < 4) { + ShardingConfig(num = 4, index = i) + } else { + null + } + ) if (i == 4) { // Last execution should execute all tests together @@ -67,9 +70,7 @@ class AndroidJUnitFrameworkTests { } } - allResults.forEach { results -> - assertThat(results.runCount).isLessThan(totalTests) - } + allResults.forEach { results -> assertThat(results.runCount).isLessThan(totalTests) } } /* Private */ @@ -90,12 +91,13 @@ class AndroidJUnitFrameworkTests { return resultRef.get() } - private fun buildArgs(shardingConfig: ShardingConfig?) = Bundle().apply { - if (shardingConfig != null) { - putString("numShards", shardingConfig.num.toString()) - putString("shardIndex", shardingConfig.index.toString()) + private fun buildArgs(shardingConfig: ShardingConfig?) = + Bundle().apply { + if (shardingConfig != null) { + putString("numShards", shardingConfig.num.toString()) + putString("shardIndex", shardingConfig.index.toString()) + } } - } // JUnit Vintage Engine reports an empty event and must be excluded. // Because of this, only count tests with an attached method name @@ -104,32 +106,21 @@ class AndroidJUnitFrameworkTests { private val CollectingRunListener.Results.runCount get() = runTestNames.size - } /* Data */ @Suppress("ClassName") internal class Sample_NormalTests { - @Test - fun `normal test`() { - } + @Test fun `normal test`() {} @TestFactory - fun testFactory(): DynamicContainer = dynamicContainer( - "container", - listOf( - dynamicTest("test 1") {}, - dynamicTest("test 2") {}, - ) - ) - - @RepeatedTest(3) - fun repeatedTest() { - } + fun testFactory(): DynamicContainer = + dynamicContainer("container", listOf(dynamicTest("test 1") {}, dynamicTest("test 2") {})) + + @RepeatedTest(3) fun repeatedTest() {} @ValueSource(strings = ["hello", "world"]) @ParameterizedTest - fun parameterizedTest(param: String) { - } -} \ No newline at end of file + fun parameterizedTest(param: String) {} +} diff --git a/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/internal/runners/AndroidJUnitPlatformTestTreeTests.kt b/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/internal/runners/AndroidJUnitPlatformTestTreeTests.kt index 2fc30771..6e6fe366 100644 --- a/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/internal/runners/AndroidJUnitPlatformTestTreeTests.kt +++ b/instrumentation/runner/src/test/kotlin/de/mannodermaus/junit5/internal/runners/AndroidJUnitPlatformTestTreeTests.kt @@ -7,18 +7,15 @@ import de.mannodermaus.junit5.HasTest import de.mannodermaus.junit5.HasTestFactory import de.mannodermaus.junit5.HasTestTemplate import de.mannodermaus.junit5.discoverTests +import kotlin.reflect.KClass import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.CsvSource import org.junit.platform.launcher.TestExecutionListener import org.junit.platform.launcher.TestIdentifier import org.junit.platform.launcher.core.LauncherFactory -import kotlin.reflect.KClass class AndroidJUnitPlatformTestTreeTests { - @CsvSource( - "false, method", - "true, method", - ) + @CsvSource("false, method", "true, method") @ParameterizedTest fun test(isolated: Boolean, expected: String) = runTestWith(HasTest::class, isolated) { identifier -> @@ -26,10 +23,7 @@ class AndroidJUnitPlatformTestTreeTests { assertThat(description.methodName).isEqualTo(expected) } - @CsvSource( - "false, method[RepetitionInfo] - repetition %d of 5", - "true, method[%d]", - ) + @CsvSource("false, method[RepetitionInfo] - repetition %d of 5", "true, method[%d]") @ParameterizedTest fun `repeated test`(isolated: Boolean, expected: String) = runTestWith(HasRepeatedTest::class, isolated) { identifier -> @@ -40,10 +34,7 @@ class AndroidJUnitPlatformTestTreeTests { } } - @CsvSource( - "false, method - %s", - "true, method[%d]", - ) + @CsvSource("false, method - %s", "true, method[%d]") @ParameterizedTest fun `test factory`(isolated: Boolean, expected: String) = runTestWith(HasTestFactory::class, isolated) { identifier -> @@ -56,15 +47,13 @@ class AndroidJUnitPlatformTestTreeTests { if (isolated) { assertThat(childDescription.methodName).isEqualTo(expected.format(num)) } else { - assertThat(childDescription.methodName).isEqualTo(expected.format(childMethodNames[index])) + assertThat(childDescription.methodName) + .isEqualTo(expected.format(childMethodNames[index])) } } } - @CsvSource( - "false, method[String] - %s", - "true, method[%d]", - ) + @CsvSource("false, method[String] - %s", "true, method[%d]") @ParameterizedTest fun `test template`(isolated: Boolean, expected: String) = runTestWith(HasTestTemplate::class, isolated) { identifier -> @@ -77,15 +66,13 @@ class AndroidJUnitPlatformTestTreeTests { if (isolated) { assertThat(childDescription.methodName).isEqualTo(expected.format(num)) } else { - assertThat(childDescription.methodName).isEqualTo(expected.format(childMethodNames[index])) + assertThat(childDescription.methodName) + .isEqualTo(expected.format(childMethodNames[index])) } } } - @CsvSource( - "false, method[String] - [%d] %s", - "true, method[%d]", - ) + @CsvSource("false, method[String] - [%d] %s", "true, method[%d]") @ParameterizedTest fun `parameterized test`(isolated: Boolean, expected: String) = runTestWith(HasParameterizedTest::class, isolated) { identifier -> @@ -98,7 +85,8 @@ class AndroidJUnitPlatformTestTreeTests { if (isolated) { assertThat(childDescription.methodName).isEqualTo(expected.format(num)) } else { - assertThat(childDescription.methodName).isEqualTo(expected.format(num, childMethodNames[index])) + assertThat(childDescription.methodName) + .isEqualTo(expected.format(num, childMethodNames[index])) } } } @@ -113,20 +101,24 @@ class AndroidJUnitPlatformTestTreeTests { // Prepare a test plan to launch val launcher = LauncherFactory.create() val plan = discoverTests(cls, launcher, executeAsWell = false) - val tree = AndroidJUnitPlatformTestTree( - testPlan = plan, - testClass = cls.java, - needLegacyFormat = isIsolatedMethodRun, - isParallelExecutionEnabled = false, - ) + val tree = + AndroidJUnitPlatformTestTree( + testPlan = plan, + testClass = cls.java, + needLegacyFormat = isIsolatedMethodRun, + isParallelExecutionEnabled = false, + ) // Execute the test plan, adding dynamic tests with the tree // as they are registered during execution - launcher.execute(plan, object : TestExecutionListener { - override fun dynamicTestRegistered(testIdentifier: TestIdentifier) { - tree.addDynamicDescription(testIdentifier, testIdentifier.parentId.get()) - } - }) + launcher.execute( + plan, + object : TestExecutionListener { + override fun dynamicTestRegistered(testIdentifier: TestIdentifier) { + tree.addDynamicDescription(testIdentifier, testIdentifier.parentId.get()) + } + }, + ) // For concrete assertions, delegate to the given block val root = plan.roots.first() @@ -138,7 +130,7 @@ class AndroidJUnitPlatformTestTreeTests { private fun AndroidJUnitPlatformTestTree.assertChildren( identifier: TestIdentifier, expectedCount: Int, - block: (Int, TestIdentifier) -> Unit + block: (Int, TestIdentifier) -> Unit, ) { with(getChildren(identifier)) { assertThat(size).isEqualTo(expectedCount) diff --git a/instrumentation/sample/build.gradle.kts b/instrumentation/sample/build.gradle.kts index 64d2201d..e70be489 100644 --- a/instrumentation/sample/build.gradle.kts +++ b/instrumentation/sample/build.gradle.kts @@ -15,7 +15,8 @@ android { versionCode = 1 versionName = "1.0" - // Make sure to use the AndroidJUnitRunner (or a sub-class) in order to hook in the JUnit 5 Test Builder + // Make sure to use the AndroidJUnitRunner (or a sub-class) in order to hook in the JUnit 5 + // Test Builder testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" // These two lines are not needed for a normal integration; @@ -25,23 +26,17 @@ android { testInstrumentationRunnerArguments["configurationParameters"] = "junit.jupiter.execution.parallel.enabled=true,junit.jupiter.execution.parallel.mode.default=concurrent" - buildFeatures { - buildConfig = true - } + buildFeatures { buildConfig = true } buildConfigField("boolean", "MY_VALUE", "true") - testOptions { - animationsDisabled = true - } + testOptions { animationsDisabled = true } } } junitPlatform { // Configure JUnit 5 tests here - filters("debug") { - excludeTags("slow") - } + filters("debug") { excludeTags("slow") } // Using local dependency instead of Maven coordinates instrumentationTests.enabled = false diff --git a/instrumentation/sample/src/androidTest/kotlin/de/mannodermaus/sample/ActivityOneTest.kt b/instrumentation/sample/src/androidTest/kotlin/de/mannodermaus/sample/ActivityOneTest.kt index 86bf8c4d..92b8c58b 100644 --- a/instrumentation/sample/src/androidTest/kotlin/de/mannodermaus/sample/ActivityOneTest.kt +++ b/instrumentation/sample/src/androidTest/kotlin/de/mannodermaus/sample/ActivityOneTest.kt @@ -10,6 +10,8 @@ import de.mannodermaus.junit5.ActivityScenarioExtension import de.mannodermaus.junit5.condition.EnabledIfBuildConfigValue import de.mannodermaus.junit5.sample.ActivityOne import de.mannodermaus.junit5.sample.R +import java.util.function.Supplier +import java.util.stream.Stream import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.RepeatedTest @@ -20,78 +22,71 @@ import org.junit.jupiter.api.extension.RegisterExtension import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.FieldSource import org.junit.jupiter.params.provider.ValueSource -import java.util.function.Supplier -import java.util.stream.Stream class ActivityOneTest { - companion object { - val someLettersOfTheAlphabet = Supplier { Stream.of("A", "B", "C") } - } - - @JvmField - @RegisterExtension - val scenarioExtension = ActivityScenarioExtension.launch() + companion object { + val someLettersOfTheAlphabet = Supplier { Stream.of("A", "B", "C") } + } - @DisplayName("test with pretty display name") - @Test - fun testDisplayName() { - onView(withId(R.id.textView)).check(matches(withText("0"))) - } + @JvmField + @RegisterExtension + val scenarioExtension = ActivityScenarioExtension.launch() - @EnabledIfBuildConfigValue(named = "MY_VALUE", matches = "true") - @Test - fun testExample(scenario: ActivityScenario) { - onView(withId(R.id.textView)).check(matches(withText("0"))) - } + @DisplayName("test with pretty display name") + @Test + fun testDisplayName() { + onView(withId(R.id.textView)).check(matches(withText("0"))) + } - // This test is disabled by default, because the sample module - // defines an excludeTags() clause for this tag in its build.gradle file - @Tag("slow") - @Test - fun disabledTest(scenario: ActivityScenario) { - onView(withId(R.id.textView)).check(matches(withText("0"))) - } + @EnabledIfBuildConfigValue(named = "MY_VALUE", matches = "true") + @Test + fun testExample(scenario: ActivityScenario) { + onView(withId(R.id.textView)).check(matches(withText("0"))) + } - @ValueSource(strings = ["value1", "value2"]) - @ParameterizedTest - fun parameterizedTestExample(value: String, scenario: ActivityScenario) { - scenario.onActivity { - assertEquals(0, it.getClickCount()) - it.setButtonLabel(value) + // This test is disabled by default, because the sample module + // defines an excludeTags() clause for this tag in its build.gradle file + @Tag("slow") + @Test + fun disabledTest(scenario: ActivityScenario) { + onView(withId(R.id.textView)).check(matches(withText("0"))) } - onView(withId(R.id.button)).check(matches(withText(value))) - onView(withId(R.id.button)).perform(click()) + @ValueSource(strings = ["value1", "value2"]) + @ParameterizedTest + fun parameterizedTestExample(value: String, scenario: ActivityScenario) { + scenario.onActivity { + assertEquals(0, it.getClickCount()) + it.setButtonLabel(value) + } - scenario.onActivity { - assertEquals(1, it.getClickCount()) - } - } + onView(withId(R.id.button)).check(matches(withText(value))) + onView(withId(R.id.button)).perform(click()) - @FieldSource("someLettersOfTheAlphabet") - @ParameterizedTest - fun parameterizedTestWithFieldSource(letter: String) { - scenarioExtension.scenario.onActivity { - it.setButtonLabel(letter) + scenario.onActivity { assertEquals(1, it.getClickCount()) } } - onView(withText(letter)).perform(click()) + @FieldSource("someLettersOfTheAlphabet") + @ParameterizedTest + fun parameterizedTestWithFieldSource(letter: String) { + scenarioExtension.scenario.onActivity { it.setButtonLabel(letter) } - scenarioExtension.scenario.onActivity { - assertEquals(1, it.getClickCount()) + onView(withText(letter)).perform(click()) + + scenarioExtension.scenario.onActivity { assertEquals(1, it.getClickCount()) } } - } - @RepeatedTest(3) - fun repeatedTestExample(repetitionInfo: RepetitionInfo, scenario: ActivityScenario) { - val count = repetitionInfo.currentRepetition + @RepeatedTest(3) + fun repeatedTestExample( + repetitionInfo: RepetitionInfo, + scenario: ActivityScenario, + ) { + val count = repetitionInfo.currentRepetition - for (i in 0 until count) { - onView(withId(R.id.button)).perform(click()) - } + for (i in 0 until count) { + onView(withId(R.id.button)).perform(click()) + } - scenario.onActivity { - assertEquals(count, it.getClickCount()) + scenario.onActivity { assertEquals(count, it.getClickCount()) } } - } } diff --git a/instrumentation/sample/src/androidTest/kotlin/de/mannodermaus/sample/TestRunningOnJUnit4.kt b/instrumentation/sample/src/androidTest/kotlin/de/mannodermaus/sample/TestRunningOnJUnit4.kt index 62568128..b2da7b3e 100644 --- a/instrumentation/sample/src/androidTest/kotlin/de/mannodermaus/sample/TestRunningOnJUnit4.kt +++ b/instrumentation/sample/src/androidTest/kotlin/de/mannodermaus/sample/TestRunningOnJUnit4.kt @@ -4,28 +4,28 @@ import org.junit.Assert import org.junit.Test class TestRunningOnJUnit4 { - @Test - fun junit4() { - Assert.assertEquals(4, 2 + 2) - } + @Test + fun junit4() { + Assert.assertEquals(4, 2 + 2) + } - @Test - fun junit4_2() { - Assert.assertEquals(4, 2 + 2) - } + @Test + fun junit4_2() { + Assert.assertEquals(4, 2 + 2) + } - @Test - fun junit4_3() { - Assert.assertEquals(4, 2 + 2) - } + @Test + fun junit4_3() { + Assert.assertEquals(4, 2 + 2) + } - @Test - fun junit4_4() { - Assert.assertEquals(4, 2 + 2) - } + @Test + fun junit4_4() { + Assert.assertEquals(4, 2 + 2) + } - @Test - fun junit4_5() { - Assert.assertEquals(4, 2 + 2) - } + @Test + fun junit4_5() { + Assert.assertEquals(4, 2 + 2) + } } diff --git a/instrumentation/sample/src/androidTest/kotlin/de/mannodermaus/sample/TestTemplateExampleTests.kt b/instrumentation/sample/src/androidTest/kotlin/de/mannodermaus/sample/TestTemplateExampleTests.kt index 7c40945e..e33a98a4 100644 --- a/instrumentation/sample/src/androidTest/kotlin/de/mannodermaus/sample/TestTemplateExampleTests.kt +++ b/instrumentation/sample/src/androidTest/kotlin/de/mannodermaus/sample/TestTemplateExampleTests.kt @@ -1,5 +1,6 @@ package de.mannodermaus.sample +import java.util.stream.Stream import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.TestTemplate import org.junit.jupiter.api.extension.ExtendWith @@ -9,7 +10,6 @@ import org.junit.jupiter.api.extension.ParameterContext import org.junit.jupiter.api.extension.TestTemplateInvocationContext import org.junit.jupiter.api.extension.TestTemplateInvocationContextProvider import org.junit.jupiter.api.extension.support.TypeBasedParameterResolver -import java.util.stream.Stream class TestTemplateExampleTests { @TestTemplate @@ -19,21 +19,17 @@ class TestTemplateExampleTests { } } -data class TemplateTestCase( - val name: String, - val expectedLength: Int, -) +data class TemplateTestCase(val name: String, val expectedLength: Int) class NameAndLengthTemplateContextProvider : TestTemplateInvocationContextProvider { override fun supportsTestTemplate(context: ExtensionContext): Boolean { return true } - override fun provideTestTemplateInvocationContexts(context: ExtensionContext): Stream { - return Stream.of( - createCase("Alice", 5), - createCase("Bob", 3) - ) + override fun provideTestTemplateInvocationContexts( + context: ExtensionContext + ): Stream { + return Stream.of(createCase("Alice", 5), createCase("Bob", 3)) } private fun createCase(name: String, expected: Int): TestTemplateInvocationContext { @@ -45,11 +41,16 @@ class NameAndLengthTemplateContextProvider : TestTemplateInvocationContextProvid } override fun getAdditionalExtensions(): List { - return listOf(object : TypeBasedParameterResolver() { - override fun resolveParameter(parameterContext: ParameterContext, extensionContext: ExtensionContext): TemplateTestCase { - return testCase + return listOf( + object : TypeBasedParameterResolver() { + override fun resolveParameter( + parameterContext: ParameterContext, + extensionContext: ExtensionContext, + ): TemplateTestCase { + return testCase + } } - }) + ) } } } diff --git a/instrumentation/sample/src/main/kotlin/de/mannodermaus/junit5/sample/ActivityOne.kt b/instrumentation/sample/src/main/kotlin/de/mannodermaus/junit5/sample/ActivityOne.kt index 1e797793..2a35439b 100644 --- a/instrumentation/sample/src/main/kotlin/de/mannodermaus/junit5/sample/ActivityOne.kt +++ b/instrumentation/sample/src/main/kotlin/de/mannodermaus/junit5/sample/ActivityOne.kt @@ -7,25 +7,25 @@ import android.widget.TextView public class ActivityOne : Activity() { - private val textView by lazy { findViewById(R.id.textView) } - private val button by lazy { findViewById