Skip to content

Commit

Permalink
Add support for test sharding (#272)
Browse files Browse the repository at this point in the history
* Add a common testutil module for shared helpers

* Introduce ShardingFilter and mirror support for 'numShards' and 'shardIndex'
  • Loading branch information
mannodermaus committed Jun 13, 2022
1 parent 6def54e commit 26cd58e
Show file tree
Hide file tree
Showing 22 changed files with 444 additions and 126 deletions.
1 change: 1 addition & 0 deletions build-logic/src/main/kotlin/Dependencies.kt
Expand Up @@ -43,6 +43,7 @@ object libs {
const val mockitoKotlin = "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
const val truth = "com.google.truth:truth:${versions.truth}"
const val truthJava8Extensions = "com.google.truth.extensions:truth-java8-extension:${versions.truth}"
const val robolectric = "org.robolectric:robolectric:4.8.1"

const val androidXTestCore = "androidx.test:core:${versions.androidXTest}"
const val androidXTestRunner = "androidx.test:runner:${versions.androidXTest}"
Expand Down
1 change: 1 addition & 0 deletions instrumentation/CHANGELOG.md
Expand Up @@ -3,6 +3,7 @@ Change Log

## Unreleased
- Update formatting of instrumentation test names to prevent breaking generation of log files in newer versions of AGP (#263)
- Add support for test sharding (#270)

## 1.3.0 (2021-09-17)

Expand Down
6 changes: 1 addition & 5 deletions instrumentation/core/build.gradle.kts
Expand Up @@ -108,11 +108,7 @@ dependencies {
androidTestRuntimeOnly(project(":runner"))
androidTestRuntimeOnly(libs.junitJupiterEngine)

testImplementation(libs.junitJupiterApi)
testImplementation(libs.mockitoCore)
testImplementation(libs.mockitoKotlin)
testImplementation(libs.truth)
testImplementation(libs.truthJava8Extensions)
testImplementation(project(":testutil"))
}

project.configureDeployment(Artifacts.Instrumentation.Core)
Expand Up @@ -2,7 +2,7 @@ package de.mannodermaus.junit5.condition

import com.google.common.truth.Truth.assertThat
import de.mannodermaus.junit5.internal.DisabledIfBuildConfigValueCondition
import de.mannodermaus.junit5.util.AndroidBuildUtils.withMockedInstrumentation
import de.mannodermaus.junit5.testutil.AndroidBuildUtils.withMockedInstrumentation
import de.mannodermaus.junit5.util.RESOURCE_LOCK_INSTRUMENTATION
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
Expand Down
Expand Up @@ -2,7 +2,7 @@ package de.mannodermaus.junit5.condition

import com.google.common.truth.Truth.assertThat
import de.mannodermaus.junit5.internal.DisabledOnManufacturerCondition
import de.mannodermaus.junit5.util.AndroidBuildUtils.withManufacturer
import de.mannodermaus.junit5.testutil.AndroidBuildUtils.withManufacturer
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.extension.ExecutionCondition
Expand Down
Expand Up @@ -2,7 +2,7 @@ package de.mannodermaus.junit5.condition

import com.google.common.truth.Truth.assertThat
import de.mannodermaus.junit5.internal.DisabledOnSdkVersionCondition
import de.mannodermaus.junit5.util.AndroidBuildUtils.withApiLevel
import de.mannodermaus.junit5.testutil.AndroidBuildUtils.withApiLevel
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.extension.ExecutionCondition
Expand Down
Expand Up @@ -2,7 +2,7 @@ package de.mannodermaus.junit5.condition

import com.google.common.truth.Truth.assertThat
import de.mannodermaus.junit5.internal.EnabledIfBuildConfigValueCondition
import de.mannodermaus.junit5.util.AndroidBuildUtils.withMockedInstrumentation
import de.mannodermaus.junit5.testutil.AndroidBuildUtils.withMockedInstrumentation
import de.mannodermaus.junit5.util.RESOURCE_LOCK_INSTRUMENTATION
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
Expand Down
Expand Up @@ -2,7 +2,7 @@ package de.mannodermaus.junit5.condition

import com.google.common.truth.Truth.assertThat
import de.mannodermaus.junit5.internal.EnabledOnManufacturerCondition
import de.mannodermaus.junit5.util.AndroidBuildUtils.withManufacturer
import de.mannodermaus.junit5.testutil.AndroidBuildUtils.withManufacturer
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.extension.ExecutionCondition
Expand Down
Expand Up @@ -2,7 +2,7 @@ package de.mannodermaus.junit5.condition

import com.google.common.truth.Truth.assertThat
import de.mannodermaus.junit5.internal.EnabledOnSdkVersionCondition
import de.mannodermaus.junit5.util.AndroidBuildUtils.withApiLevel
import de.mannodermaus.junit5.testutil.AndroidBuildUtils.withApiLevel
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.extension.ExecutionCondition
Expand Down

This file was deleted.

This file was deleted.

9 changes: 3 additions & 6 deletions instrumentation/runner/build.gradle.kts
Expand Up @@ -105,12 +105,9 @@ dependencies {
compileOnly(libs.junitJupiterParams)
compileOnly(libs.junitPlatformRunner)

testImplementation(libs.truth)
testImplementation(libs.mockitoCore)
testImplementation(libs.junitJupiterApi)
testImplementation(libs.junitJupiterParams)
testImplementation(libs.junitPlatformRunner)

testImplementation(project(":testutil"))
testImplementation(libs.robolectric)
testRuntimeOnly(libs.junitVintageEngine)
testRuntimeOnly(libs.junitJupiterEngine)
}

Expand Down
Expand Up @@ -38,7 +38,7 @@ internal object GeneratedFilters {
}

if (inputStream == null) {
// File does't exist, or couldn't be located; return
// File doesn't exist, or couldn't be located; return
return emptyList()
}

Expand Down
@@ -0,0 +1,53 @@
package de.mannodermaus.junit5.internal.discovery

import android.os.Bundle
import de.mannodermaus.junit5.internal.extensions.isDynamicTest
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.
*
* Based on a draft by KyoungJoo Jeon (@jkj8790).
*/
internal class ShardingFilter(
private val numShards: Int,
private val shardIndex: Int,
) : PostDiscoveryFilter {

companion object {
private const val ARG_NUM_SHARDS = "numShards"
private const val ARG_SHARD_INDEX = "shardIndex"

fun fromArguments(arguments: Bundle): ShardingFilter? {
val numShards = arguments.getString(ARG_NUM_SHARDS)?.toInt() ?: -1
val shardIndex = arguments.getString(ARG_SHARD_INDEX)?.toInt() ?: -1

return if (numShards > 0 && shardIndex >= 0 && shardIndex < numShards) {
ShardingFilter(numShards, shardIndex)
} else {
null
}
}
}

override fun apply(descriptor: TestDescriptor): FilterResult {
val identifier = TestIdentifier.from(descriptor)

if (identifier.isTest || identifier.isDynamicTest) {
val remainder = abs(identifier.hashCode()) % numShards
return if (remainder == shardIndex) {
FilterResult.included(null)
} else {
FilterResult.excluded("excluded")
}
}

return FilterResult.included(null)
}
}
Expand Up @@ -4,6 +4,7 @@ import androidx.test.platform.app.InstrumentationRegistry
import de.mannodermaus.junit5.internal.discovery.GeneratedFilters
import de.mannodermaus.junit5.internal.discovery.ParsedSelectors
import de.mannodermaus.junit5.internal.discovery.PropertiesParser
import de.mannodermaus.junit5.internal.discovery.ShardingFilter
import org.junit.platform.engine.DiscoverySelector
import org.junit.platform.engine.Filter
import org.junit.platform.engine.discovery.MethodSelector
Expand Down Expand Up @@ -60,7 +61,8 @@ internal fun createRunnerParams(testClass: Class<*>): AndroidJUnit5RunnerParams
// 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 AndroidJUnit5RunnerParams(
selectors,
Expand Down

0 comments on commit 26cd58e

Please sign in to comment.