From 098257fe9f6baf6b849dc236f55d66b7fb4479cb Mon Sep 17 00:00:00 2001 From: Marcel Schnelle Date: Sat, 15 Jun 2024 10:08:14 +0200 Subject: [PATCH] Use legacy test name formatting for Test Orchestrator, too (#342) This removes the need to use the deprecated @UseTechnicalNames annotation, so let's get rid of that anyway. The tool now controls the legacy mode! Finally, prevent people from using parallel test execution with the Orchestrator. --- instrumentation/CHANGELOG.md | 1 + .../internal/extensions/TestIdentifierExt.kt | 6 +++--- .../internal/formatters/TestNameFormatter.kt | 6 +++--- .../junit5/internal/runners/AndroidJUnit5.kt | 18 ++++++++++++++---- .../runners/AndroidJUnit5RunnerParams.kt | 3 +++ .../runners/AndroidJUnitPlatformTestTree.kt | 15 ++++----------- .../AndroidJUnitPlatformTestTreeTests.kt | 2 +- 7 files changed, 29 insertions(+), 22 deletions(-) diff --git a/instrumentation/CHANGELOG.md b/instrumentation/CHANGELOG.md index 4ba7c679..6cea50f3 100644 --- a/instrumentation/CHANGELOG.md +++ b/instrumentation/CHANGELOG.md @@ -8,6 +8,7 @@ Change Log - Fix invalid naming of dynamic tests when executing only a singular test method from the IDE (#317, #339) - Prevent test methods incorrectly defined as Kotlin top-level functions from messing up Android's internal test counting, causing issues like "Expected N+1 tests, received N" (#316) - Prevent test classes ignored by a tag from being considered for test execution, causing issues like "Expected N+1 tests, received N" (#298) +- Improve integration with Android Test Orchestrator and remove the need for `@UseTechnicalNames` (#337) ## 1.4.0 (2023-11-05) 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 ab2cf710..c54aeb90 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 @@ -34,8 +34,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 [isIsolatedMethodRun] + * of the Android Instrumentation, esp. when the [legacyFormat] * flag is enabled. */ -internal fun TestIdentifier.format(isIsolatedMethodRun: Boolean = false): String = - TestNameFormatter.format(this, isIsolatedMethodRun) +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 62220a0d..ada8f85d 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 @@ -8,8 +8,8 @@ import org.junit.platform.launcher.TestIdentifier * Android instrumentation (e.g. on isolated test runs). */ internal object TestNameFormatter { - fun format(identifier: TestIdentifier, isIsolatedMethodRun: Boolean = false): String { - // During isolated executions of a single test method, + 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. // If this didn't happen, running them from the IDE will cause "No tests found" errors. @@ -19,7 +19,7 @@ internal object TestNameFormatter { // - #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) - if (isIsolatedMethodRun) { + if (legacyFormat) { val reportName = identifier.legacyReportingName val paramStartIndex = reportName.indexOf('(') if (paramStartIndex > -1) { diff --git a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/AndroidJUnit5.kt b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/AndroidJUnit5.kt index eb6c658b..a9ac5666 100644 --- a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/AndroidJUnit5.kt +++ b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/AndroidJUnit5.kt @@ -1,10 +1,7 @@ package de.mannodermaus.junit5.internal.runners import android.annotation.SuppressLint -import android.util.Log import androidx.annotation.VisibleForTesting -import de.mannodermaus.junit5.internal.LOG_TAG -import de.mannodermaus.junit5.internal.LibcoreAccess import de.mannodermaus.junit5.internal.runners.notification.ParallelRunNotifier import org.junit.platform.engine.discovery.MethodSelector import org.junit.platform.launcher.core.LauncherFactory @@ -44,12 +41,25 @@ internal class AndroidJUnit5( private fun generateTestTree(params: AndroidJUnit5RunnerParams): AndroidJUnitPlatformTestTree { val selectors = params.createSelectors(testClass) val isIsolatedMethodRun = selectors.size == 1 && selectors.first() is MethodSelector + val isUsingOrchestrator = params.isUsingOrchestrator val request = params.createDiscoveryRequest(selectors) + // Validate if run can be executed + 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(), + ) + } + return AndroidJUnitPlatformTestTree( testPlan = launcher.discover(request), testClass = testClass, - isIsolatedMethodRun = isIsolatedMethodRun, + needLegacyFormat = isIsolatedMethodRun || isUsingOrchestrator, isParallelExecutionEnabled = params.isParallelExecutionEnabled, ) } diff --git a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/AndroidJUnit5RunnerParams.kt b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/AndroidJUnit5RunnerParams.kt index 380c3086..64d8fe6a 100644 --- a/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/AndroidJUnit5RunnerParams.kt +++ b/instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/AndroidJUnit5RunnerParams.kt @@ -33,6 +33,9 @@ internal data class AndroidJUnit5RunnerParams( val isParallelExecutionEnabled: Boolean get() = configurationParameters["junit.jupiter.execution.parallel.enabled"] == "true" + val isUsingOrchestrator: Boolean + get() = arguments.getString("orchestratorService") != null + internal companion object { fun create(): AndroidJUnit5RunnerParams { val instrumentation = InstrumentationRegistry.getInstrumentation() 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 b489baa2..f7352ba7 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 @@ -12,7 +12,6 @@ import org.junit.platform.engine.support.descriptor.MethodSource import org.junit.platform.launcher.TestIdentifier import org.junit.platform.launcher.TestPlan import org.junit.platform.suite.api.SuiteDisplayName -import org.junit.platform.suite.api.UseTechnicalNames import org.junit.runner.Description import java.util.Optional import java.util.function.Predicate @@ -26,7 +25,7 @@ import java.util.function.Predicate internal class AndroidJUnitPlatformTestTree( testPlan: TestPlan, testClass: Class<*>, - private val isIsolatedMethodRun: Boolean, + private val needLegacyFormat: Boolean, val isParallelExecutionEnabled: Boolean, ) { @@ -39,7 +38,7 @@ 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 (isIsolatedMethodRun) { + 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 @@ -69,7 +68,7 @@ internal class AndroidJUnitPlatformTestTree( identifier.isContainer -> getTechnicalName(identifier) - else -> identifier.format(isIsolatedMethodRun) + else -> identifier.format(needLegacyFormat) } // Do not expose our custom TestPlan, because JUnit Platform wouldn't like that very much. @@ -82,13 +81,7 @@ internal class AndroidJUnitPlatformTestTree( } private fun generateSuiteDescription(testPlan: TestPlan, testClass: Class<*>): Description { - val displayName = if (testClass.isAnnotationPresent(UseTechnicalNames::class.java)) { - testClass.name - } else { - getSuiteDisplayName(testClass) - } - - return Description.createSuiteDescription(displayName).also { + return Description.createSuiteDescription(getSuiteDisplayName(testClass)).also { buildDescriptionTree(it, testPlan) } } 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 a999ac03..e46abd55 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 @@ -116,7 +116,7 @@ class AndroidJUnitPlatformTestTreeTests { val tree = AndroidJUnitPlatformTestTree( testPlan = plan, testClass = cls.java, - isIsolatedMethodRun = isIsolatedMethodRun, + needLegacyFormat = isIsolatedMethodRun, isParallelExecutionEnabled = false, )