diff --git a/runner/android_junit_runner/java/androidx/test/filters/ShardingFilter.java b/runner/android_junit_runner/java/androidx/test/filters/ShardingFilter.java new file mode 100644 index 000000000..a40534945 --- /dev/null +++ b/runner/android_junit_runner/java/androidx/test/filters/ShardingFilter.java @@ -0,0 +1,40 @@ +package androidx.test.filters; + +import androidx.annotation.RestrictTo; +import androidx.annotation.RestrictTo.Scope; +import org.junit.runner.Description; +import org.junit.runner.manipulation.Filter; + +/** + * A JUnit sharding filter uses the hashcode of the test description to assign it to a shard. + * + * @hide + */ +@RestrictTo(Scope.LIBRARY) +public class ShardingFilter extends Filter { + private final int numShards; + private final int shardIndex; + + public ShardingFilter(int numShards, int shardIndex) { + this.numShards = numShards; + this.shardIndex = shardIndex; + } + + @Override + public boolean shouldRun(Description description) { + if (description.isTest()) { + return (Math.floorMod(description.hashCode(), numShards)) == shardIndex; + } + + // The description is a suite, so assume that it can be run so that filtering is + // applied to its children. If after filtering it has no children then it will be + // automatically filtered out. + return true; + } + + /** {@inheritDoc} */ + @Override + public String describe() { + return String.format("Shard %d of %d shards", shardIndex, numShards); + } +} diff --git a/runner/android_junit_runner/java/androidx/test/internal/runner/TestRequestBuilder.java b/runner/android_junit_runner/java/androidx/test/internal/runner/TestRequestBuilder.java index 1a6ebb937..043ab6a4f 100644 --- a/runner/android_junit_runner/java/androidx/test/internal/runner/TestRequestBuilder.java +++ b/runner/android_junit_runner/java/androidx/test/internal/runner/TestRequestBuilder.java @@ -25,6 +25,7 @@ import androidx.test.filters.CustomFilter; import androidx.test.filters.RequiresDevice; import androidx.test.filters.SdkSuppressFilter; +import androidx.test.filters.ShardingFilter; import androidx.test.filters.TestsRegExFilter; import androidx.test.internal.runner.ClassPathScanner.ChainedClassNameFilter; import androidx.test.internal.runner.ClassPathScanner.ExcludeClassNamesFilter; @@ -277,34 +278,6 @@ public String describe() { } } - private static class ShardingFilter extends Filter { - private final int numShards; - private final int shardIndex; - - ShardingFilter(int numShards, int shardIndex) { - this.numShards = numShards; - this.shardIndex = shardIndex; - } - - @Override - public boolean shouldRun(Description description) { - if (description.isTest()) { - return (Math.abs(description.hashCode()) % numShards) == shardIndex; - } - - // The description is a suite, so assume that it can be run so that filtering is - // applied to its children. If after filtering it has no children then it will be - // automatically filtered out. - return true; - } - - /** {@inheritDoc} */ - @Override - public String describe() { - return String.format("Shard %s of %s shards", shardIndex, numShards); - } - } - /** * A {@link Request} that doesn't report an error if all tests are filtered out. Done for * consistency with InstrumentationTestRunner.