Skip to content

Commit

Permalink
Use legacy test name formatting for Test Orchestrator, too
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
mannodermaus committed Jun 15, 2024
1 parent a71c2d0 commit a44ca16
Show file tree
Hide file tree
Showing 6 changed files with 28 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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,
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
) {

Expand All @@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -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)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class AndroidJUnitPlatformTestTreeTests {
val tree = AndroidJUnitPlatformTestTree(
testPlan = plan,
testClass = cls.java,
isIsolatedMethodRun = isIsolatedMethodRun,
needLegacyFormat = isIsolatedMethodRun,
isParallelExecutionEnabled = false,
)

Expand Down

0 comments on commit a44ca16

Please sign in to comment.