Skip to content

Commit 26cd58e

Browse files
authored
Add support for test sharding (#272)
* Add a common testutil module for shared helpers * Introduce ShardingFilter and mirror support for 'numShards' and 'shardIndex'
1 parent 6def54e commit 26cd58e

22 files changed

+444
-126
lines changed

build-logic/src/main/kotlin/Dependencies.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ object libs {
4343
const val mockitoKotlin = "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
4444
const val truth = "com.google.truth:truth:${versions.truth}"
4545
const val truthJava8Extensions = "com.google.truth.extensions:truth-java8-extension:${versions.truth}"
46+
const val robolectric = "org.robolectric:robolectric:4.8.1"
4647

4748
const val androidXTestCore = "androidx.test:core:${versions.androidXTest}"
4849
const val androidXTestRunner = "androidx.test:runner:${versions.androidXTest}"

instrumentation/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ Change Log
33

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

78
## 1.3.0 (2021-09-17)
89

instrumentation/core/build.gradle.kts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -108,11 +108,7 @@ dependencies {
108108
androidTestRuntimeOnly(project(":runner"))
109109
androidTestRuntimeOnly(libs.junitJupiterEngine)
110110

111-
testImplementation(libs.junitJupiterApi)
112-
testImplementation(libs.mockitoCore)
113-
testImplementation(libs.mockitoKotlin)
114-
testImplementation(libs.truth)
115-
testImplementation(libs.truthJava8Extensions)
111+
testImplementation(project(":testutil"))
116112
}
117113

118114
project.configureDeployment(Artifacts.Instrumentation.Core)

instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/DisabledIfBuildConfigValueConditionTests.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package de.mannodermaus.junit5.condition
22

33
import com.google.common.truth.Truth.assertThat
44
import de.mannodermaus.junit5.internal.DisabledIfBuildConfigValueCondition
5-
import de.mannodermaus.junit5.util.AndroidBuildUtils.withMockedInstrumentation
5+
import de.mannodermaus.junit5.testutil.AndroidBuildUtils.withMockedInstrumentation
66
import de.mannodermaus.junit5.util.RESOURCE_LOCK_INSTRUMENTATION
77
import org.junit.jupiter.api.Test
88
import org.junit.jupiter.api.assertThrows

instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/DisabledOnManufacturerConditionTests.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package de.mannodermaus.junit5.condition
22

33
import com.google.common.truth.Truth.assertThat
44
import de.mannodermaus.junit5.internal.DisabledOnManufacturerCondition
5-
import de.mannodermaus.junit5.util.AndroidBuildUtils.withManufacturer
5+
import de.mannodermaus.junit5.testutil.AndroidBuildUtils.withManufacturer
66
import org.junit.jupiter.api.Test
77
import org.junit.jupiter.api.assertThrows
88
import org.junit.jupiter.api.extension.ExecutionCondition

instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/DisabledOnSdkVersionConditionTests.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package de.mannodermaus.junit5.condition
22

33
import com.google.common.truth.Truth.assertThat
44
import de.mannodermaus.junit5.internal.DisabledOnSdkVersionCondition
5-
import de.mannodermaus.junit5.util.AndroidBuildUtils.withApiLevel
5+
import de.mannodermaus.junit5.testutil.AndroidBuildUtils.withApiLevel
66
import org.junit.jupiter.api.Test
77
import org.junit.jupiter.api.assertThrows
88
import org.junit.jupiter.api.extension.ExecutionCondition

instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/EnabledIfBuildConfigValueConditionTests.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package de.mannodermaus.junit5.condition
22

33
import com.google.common.truth.Truth.assertThat
44
import de.mannodermaus.junit5.internal.EnabledIfBuildConfigValueCondition
5-
import de.mannodermaus.junit5.util.AndroidBuildUtils.withMockedInstrumentation
5+
import de.mannodermaus.junit5.testutil.AndroidBuildUtils.withMockedInstrumentation
66
import de.mannodermaus.junit5.util.RESOURCE_LOCK_INSTRUMENTATION
77
import org.junit.jupiter.api.Test
88
import org.junit.jupiter.api.assertThrows

instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/EnabledOnManufacturerConditionTests.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package de.mannodermaus.junit5.condition
22

33
import com.google.common.truth.Truth.assertThat
44
import de.mannodermaus.junit5.internal.EnabledOnManufacturerCondition
5-
import de.mannodermaus.junit5.util.AndroidBuildUtils.withManufacturer
5+
import de.mannodermaus.junit5.testutil.AndroidBuildUtils.withManufacturer
66
import org.junit.jupiter.api.Test
77
import org.junit.jupiter.api.assertThrows
88
import org.junit.jupiter.api.extension.ExecutionCondition

instrumentation/core/src/test/java/de/mannodermaus/junit5/condition/EnabledOnSdkVersionConditionTests.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package de.mannodermaus.junit5.condition
22

33
import com.google.common.truth.Truth.assertThat
44
import de.mannodermaus.junit5.internal.EnabledOnSdkVersionCondition
5-
import de.mannodermaus.junit5.util.AndroidBuildUtils.withApiLevel
5+
import de.mannodermaus.junit5.testutil.AndroidBuildUtils.withApiLevel
66
import org.junit.jupiter.api.Test
77
import org.junit.jupiter.api.assertThrows
88
import org.junit.jupiter.api.extension.ExecutionCondition

instrumentation/core/src/test/java/de/mannodermaus/junit5/util/AndroidBuildUtils.kt

Lines changed: 0 additions & 82 deletions
This file was deleted.

instrumentation/core/src/test/java/de/mannodermaus/junit5/util/StubInstrumentation.java

Lines changed: 0 additions & 25 deletions
This file was deleted.

instrumentation/runner/build.gradle.kts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -105,12 +105,9 @@ dependencies {
105105
compileOnly(libs.junitJupiterParams)
106106
compileOnly(libs.junitPlatformRunner)
107107

108-
testImplementation(libs.truth)
109-
testImplementation(libs.mockitoCore)
110-
testImplementation(libs.junitJupiterApi)
111-
testImplementation(libs.junitJupiterParams)
112-
testImplementation(libs.junitPlatformRunner)
113-
108+
testImplementation(project(":testutil"))
109+
testImplementation(libs.robolectric)
110+
testRuntimeOnly(libs.junitVintageEngine)
114111
testRuntimeOnly(libs.junitJupiterEngine)
115112
}
116113

instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/discovery/GeneratedFilters.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ internal object GeneratedFilters {
3838
}
3939

4040
if (inputStream == null) {
41-
// File does't exist, or couldn't be located; return
41+
// File doesn't exist, or couldn't be located; return
4242
return emptyList()
4343
}
4444

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package de.mannodermaus.junit5.internal.discovery
2+
3+
import android.os.Bundle
4+
import de.mannodermaus.junit5.internal.extensions.isDynamicTest
5+
import org.junit.platform.engine.FilterResult
6+
import org.junit.platform.engine.TestDescriptor
7+
import org.junit.platform.launcher.PostDiscoveryFilter
8+
import org.junit.platform.launcher.TestIdentifier
9+
import kotlin.math.abs
10+
11+
/**
12+
* JUnit 5 implementation of the default instrumentation's
13+
* `androidx.test.internal.runner.TestRequestBuilder$ShardingFilter`,
14+
* ported to the new API to support dynamic test templates, too.
15+
*
16+
* Based on a draft by KyoungJoo Jeon (@jkj8790).
17+
*/
18+
internal class ShardingFilter(
19+
private val numShards: Int,
20+
private val shardIndex: Int,
21+
) : PostDiscoveryFilter {
22+
23+
companion object {
24+
private const val ARG_NUM_SHARDS = "numShards"
25+
private const val ARG_SHARD_INDEX = "shardIndex"
26+
27+
fun fromArguments(arguments: Bundle): ShardingFilter? {
28+
val numShards = arguments.getString(ARG_NUM_SHARDS)?.toInt() ?: -1
29+
val shardIndex = arguments.getString(ARG_SHARD_INDEX)?.toInt() ?: -1
30+
31+
return if (numShards > 0 && shardIndex >= 0 && shardIndex < numShards) {
32+
ShardingFilter(numShards, shardIndex)
33+
} else {
34+
null
35+
}
36+
}
37+
}
38+
39+
override fun apply(descriptor: TestDescriptor): FilterResult {
40+
val identifier = TestIdentifier.from(descriptor)
41+
42+
if (identifier.isTest || identifier.isDynamicTest) {
43+
val remainder = abs(identifier.hashCode()) % numShards
44+
return if (remainder == shardIndex) {
45+
FilterResult.included(null)
46+
} else {
47+
FilterResult.excluded("excluded")
48+
}
49+
}
50+
51+
return FilterResult.included(null)
52+
}
53+
}

instrumentation/runner/src/main/kotlin/de/mannodermaus/junit5/internal/runners/AndroidJUnit5RunnerParams.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import androidx.test.platform.app.InstrumentationRegistry
44
import de.mannodermaus.junit5.internal.discovery.GeneratedFilters
55
import de.mannodermaus.junit5.internal.discovery.ParsedSelectors
66
import de.mannodermaus.junit5.internal.discovery.PropertiesParser
7+
import de.mannodermaus.junit5.internal.discovery.ShardingFilter
78
import org.junit.platform.engine.DiscoverySelector
89
import org.junit.platform.engine.Filter
910
import org.junit.platform.engine.discovery.MethodSelector
@@ -60,7 +61,8 @@ internal fun createRunnerParams(testClass: Class<*>): AndroidJUnit5RunnerParams
6061
// which aren't subject to the filtering imposed through adb.
6162
// A special resource file may be looked up at runtime, containing
6263
// the filters to apply by the AndroidJUnit5 runner.
63-
val filters = GeneratedFilters.fromContext(instrumentation.context)
64+
val filters = GeneratedFilters.fromContext(instrumentation.context) +
65+
listOfNotNull(ShardingFilter.fromArguments(arguments))
6466

6567
return AndroidJUnit5RunnerParams(
6668
selectors,

0 commit comments

Comments
 (0)