Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Parameterized Tests - multiple #2076

Merged
merged 2 commits into from
Jul 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -588,12 +588,16 @@ gcloud:
# - class com.package2.for.shard2.Class

### parameterized-tests
## Specifies how to handle tests which contain Parameterization.
## 3 options are available
## Specifies how to handle tests which contain the parameterization annotation.
## 4 options are available
## default: treat Parameterized tests as normal and shard accordingly
## ignore-all: Parameterized tests are ignored and not sharded
## shard-into-single: Parameterized tests are collected and put into a single shard
## Note: if left blank default is used. Default usage may result in significant increase/difference of shard times observed
## shard-into-multiple: Parameterized tests are collected and sharded into different shards based upon matching names. (Experimental)
## Note: If left blank default is used. Default usage may result in significant increase/difference of shard times observed
## Note: If shard-into-single is used, a single additional shard is created that will run the Parameterized tests separately.
## Note: If shard-into-multiple is used, each parameterized test will be matched by its corresponding name and sharded into a separate shard.
## This may dramatically increase the amount of expected shards depending upon how many parameterized tests are discovered.
# parameterized-tests: default

flank:
Expand Down
6 changes: 5 additions & 1 deletion test_runner/flank.yml
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,11 @@ gcloud:
## default: treat Parameterized tests as normal and shard accordingly
## ignore-all: Parameterized tests are ignored and not sharded
## shard-into-single: Parameterized tests are collected and put into a single shard
## Note: if left blank default is used. Default usage may result in significant increase/difference of shard times observed
## shard-into-multiple: Parameterized tests are collected and sharded into different shards based upon matching names. (Experimental)
## Note: If left blank default is used. Default usage may result in significant increase/difference of shard times observed
## Note: If shard-into-single is used, a single additional shard is created that will run the Parameterized tests separately.
## Note: If shard-into-multiple is used, each parameterized test will be matched by its corresponding name and sharded into a separate shard.
## This may dramatically increase the amount of expected shards depending upon how many parameterized tests are discovered.
# parameterized-tests: default

flank:
Expand Down
11 changes: 10 additions & 1 deletion test_runner/src/main/kotlin/ftl/args/ValidateAndroidArgs.kt
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,16 @@ private fun AndroidArgs.assertParameterizedTests() {
if (parameterizedTests.isNullOrEmpty() || parameterizedTests !in listOf(
"ignore-all",
"default",
"shard-into-single"
"shard-into-single",
"shard-into-multiple"
)
) throw FlankConfigurationError("Parameterized test flag must be one of the following: `default`, `ignore-all`, `shard-into-single`, leaving it blank will result in `default` sharding.")
else checkParameterizedTestFeaturesWarning()
}

private fun AndroidArgs.checkParameterizedTestFeaturesWarning() {
when (parameterizedTests) {
"shard-into-single" -> logLn("WARNING: All parameterized tests will be collected and sharded separately into a single shard. This may result in a long shard execution times if many parameterized tests are found.")
"shard-into-multiple" -> logLn("WARNING: All parameterized tests will be collected and sharded into different shards. This will result in additional shards created. max-shards is not adhered to for this selection.")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,13 @@ data class AndroidGcloudConfig @JsonIgnore constructor(

@set:CommandLine.Option(
names = ["--parameterized-tests"],
description = ["Specifies how to handle tests which contain the parameterization annotation. Possible values: `default`, `ignore-all`, `shard-into-single`, leaving it blank will result in `default` sharding"]
description = [
"Specifies how to handle tests which contain the parameterization annotation. Possible values: `default`, `ignore-all`, `shard-into-single`, `shard-into-multiple`.\n" +
"leaving it blank will result in `default` sharding.\n" +
"Note: Making use of shard-into-single` or `shard-into-multiple will result in additional shards being created even if a max number of shards has been specified.\n" +
"Note: If shard-into-single is used, a single additional shard is created that will run the Parameterized tests separately.\n" +
"Note: If shard-into-multiple is used, each parameterized test will be matched by its corresponding name and sharded into a separate shard. This may dramatically increase the amount of expected shards depending upon how many parameterized tests are discovered."
]
)
@set:JsonProperty("parameterized-tests")
var parameterizedTests: String? by data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,15 +96,15 @@ private fun InstrumentationTestContext.calculateShardsNormally(
private fun InstrumentationTestContext.calculateShards(
args: AndroidArgs,
testFilter: TestFilter = TestFilters.fromTestTargets(args.testTargets, args.testTargetsForShard)
): InstrumentationTestContext =
if (args.parameterizedTests.shouldShardIntoSingle()) {
var flankTestMethods = getFlankTestMethods(testFilter, args.parameterizedTests)
val parameterizedTests = flankTestMethods.filter { it.isParameterizedClass }
flankTestMethods = flankTestMethods.filterNot { it.isParameterizedClass }
val shards = calculateParameterizedShards(flankTestMethods, args)
val parameterizedShard = calculateParameterizedShards(parameterizedTests, args, 1)
shards.copy(shards = shards.shards + parameterizedShard.shards)
} else calculateShardsNormally(args, testFilter)
): InstrumentationTestContext = if (args.parameterizedTests.shouldShardSmartly()) {
var flankTestMethods = getFlankTestMethods(testFilter, args.parameterizedTests)
val parameterizedTests = flankTestMethods.filter { it.isParameterizedClass }
flankTestMethods = flankTestMethods.filterNot { it.isParameterizedClass }
val shards = calculateParameterizedShards(flankTestMethods, args)
val shardCount = if (args.parameterizedTests.isSingleParameterizedShard()) 1 else parameterizedTests.size
val parameterizedShard = calculateParameterizedShards(parameterizedTests, args, shardCount)
shards.copy(shards = shards.shards + parameterizedShard.shards)
} else calculateShardsNormally(args, testFilter)

private fun InstrumentationTestContext.calculateParameterizedShards(
filteredTests: List<FlankTestMethod>,
Expand All @@ -121,8 +121,6 @@ private fun InstrumentationTestContext.calculateParameterizedShards(
)
}

private fun String.shouldShardIntoSingle() = (this == "shard-into-single")

private fun InstrumentationTestContext.calculateDummyShards(
args: AndroidArgs,
testFilter: TestFilter = TestFilters.fromTestTargets(args.testTargets, args.testTargetsForShard)
Expand Down Expand Up @@ -177,6 +175,8 @@ private val ignoredAnnotations = listOf(
"android.support.test.filters.Suppress"
)

private fun String.shouldShardSmartly() = (this == "shard-into-single" || this == "shard-into-multiple")
private fun String.isSingleParameterizedShard() = (this == "shard-into-single")
private fun String.shouldIgnore(): Boolean = (this == "ignore-all")

@VisibleForTesting
Expand Down
14 changes: 14 additions & 0 deletions test_runner/src/test/kotlin/ftl/args/AndroidArgsTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2832,6 +2832,20 @@ AndroidArgs
val chunks = runBlocking { parsedYml.runAndroidTests() }.shardChunks
assertTrue(chunks.size == 1)
}

@Test
fun `should shard tests into multiple new shards with shard-into-multiple`() {
val yaml = """
gcloud:
app: $appApk
test: $testExtremeParameterizedOtherApk
parameterized-tests: shard-into-multiple
""".trimIndent()

val parsedYml = AndroidArgs.load(yaml).validate()
val chunks = runBlocking { parsedYml.runAndroidTests() }.shardChunks
assertTrue(chunks.size == 5)
}
}

private fun AndroidArgs.Companion.load(
Expand Down