Skip to content

Commit c8f188f

Browse files
authored
Add test scheduler to support AndroidTest json files (#57)
* Add ability to use test run configs This CL adds a new argument to use the json files inside the androidTest to run tests. It is less efficient than then androidx.dev implementation because I've not ported the remote zip file. We might do that as a followup * add support for test suite tags, add tests * add more logs
1 parent c6ba914 commit c8f188f

File tree

8 files changed

+557
-78
lines changed

8 files changed

+557
-78
lines changed

AndroidXCI/cli/src/main/kotlin/dev/androidx/ci/cli/Main.kt

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,26 @@ private class Cli : CliktCommand() {
128128
envvar = "ANDROIDX_BUCKET_PATH"
129129
).required()
130130

131+
val useTestConfigFiles by option(
132+
help = """
133+
Internal for AndroidX.
134+
If set, the action will look for json files matching *AndroidTest.json inside the Github archives.
135+
See TestScheduler.TestRunConfig for the json file structure.
136+
Defaults to false.
137+
""".trimIndent(),
138+
envvar = "ANDROIDX_USE_TEST_CONFIG_FILES"
139+
)
140+
141+
val testSuiteTags by option(
142+
help = """
143+
Internal for AndroidX.
144+
Comma separated list of testSuiteTags that should be run.
145+
Only used if `useTestConfigFiles` is set to true.
146+
Defaults to empty list, which runs all testSuiteTags.
147+
""".trimIndent(),
148+
envvar = "ANDROIDX_TEST_SUITE_TAGS"
149+
)
150+
131151
override fun run() {
132152
logFile?.let(::configureLogger)
133153
val repoParts = githubRepository.split("/")
@@ -155,7 +175,9 @@ private class Cli : CliktCommand() {
155175
?.let(::createDevicePicker),
156176
artifactNameFilter = artifactNameFilter,
157177
bucketName = gcpBucketName,
158-
bucketPath = gcpBucketPath
178+
bucketPath = gcpBucketPath,
179+
useTestConfigFiles = useTestConfigFiles?.toBoolean() ?: false,
180+
testSuiteTags = testSuiteTags?.split(',')?.map { it.trim() } ?: emptyList()
159181
)
160182
testRunner.runTests()
161183
}

AndroidXCI/lib/src/main/kotlin/dev/androidx/ci/testRunner/TestRunner.kt

Lines changed: 18 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,11 @@ import dev.androidx.ci.gcloud.GoogleCloudApi
2525
import dev.androidx.ci.generated.ftl.TestMatrix
2626
import dev.androidx.ci.github.GithubApi
2727
import dev.androidx.ci.github.dto.ArtifactsResponse
28-
import dev.androidx.ci.github.zipArchiveStream
2928
import dev.androidx.ci.testRunner.vo.TestResult
30-
import dev.androidx.ci.testRunner.vo.UploadedApk
3129
import kotlinx.coroutines.CoroutineDispatcher
32-
import kotlinx.coroutines.CoroutineScope
33-
import kotlinx.coroutines.async
34-
import kotlinx.coroutines.awaitAll
35-
import kotlinx.coroutines.coroutineScope
3630
import org.apache.logging.log4j.kotlin.logger
3731
import java.io.File
3832
import java.util.concurrent.TimeUnit
39-
import java.util.zip.ZipEntry
4033

4134
/**
4235
* Main class that is responsible to run tests, download reports etc.
@@ -75,7 +68,11 @@ class TestRunner internal constructor(
7568
/**
7669
* Device picker
7770
*/
78-
private val devicePicker: DevicePicker? = null
71+
private val devicePicker: DevicePicker? = null,
72+
/**
73+
* The actual class that will decide which tests to run out of the artifacts
74+
*/
75+
testSchedulerFactory: TestScheduler.Factory,
7976
) {
8077
private val logger = logger()
8178
private val testMatrixStore = TestMatrixStore(
@@ -96,6 +93,12 @@ class TestRunner internal constructor(
9693
hostRunId = hostRunId,
9794
targetRunId = targetRunId
9895
)
96+
private val testScheduler = testSchedulerFactory.create(
97+
githubApi = githubApi,
98+
firebaseTestLabController = testLabController,
99+
apkStore = apkStore,
100+
devicePicker = devicePicker
101+
)
99102

100103
/**
101104
* Runs all the test. This never throws, instead, returns an error result if something goes
@@ -109,16 +112,9 @@ class TestRunner internal constructor(
109112
val allTestMatrices = artifactsResponse.artifacts
110113
.filter(githubArtifactFilter)
111114
.flatMap { artifact ->
112-
logger.info { "will upload apks for $artifact" }
113-
val uploadedApks = uploadApksToGoogleCloud(artifact)
114-
logger.info { "will start tests for these apks: $uploadedApks" }
115-
testLabController.pairAndStartTests(
116-
apks = uploadedApks,
117-
placeholderApk = apkStore.getPlaceholderApk(),
118-
devicePicker = devicePicker
119-
).also { testMatrices ->
120-
logger.info { "started all tests for $testMatrices" }
121-
}
115+
testScheduler.enqueueTests(artifact)
116+
}.also { testMatrices ->
117+
logger.info { "started all tests for $testMatrices" }
122118
}
123119
logger.info("will wait for test results")
124120
testLabController.collectTestResults(
@@ -148,30 +144,6 @@ class TestRunner internal constructor(
148144
return result
149145
}
150146

151-
private suspend fun uploadApksToGoogleCloud(artifact: ArtifactsResponse.Artifact): List<UploadedApk> {
152-
return coroutineScope {
153-
val uploads = githubApi.zipArchiveStream(
154-
path = artifact.archiveDownloadUrl,
155-
unwrapNestedZipEntries = true
156-
).filter {
157-
it.entry.name.endsWith(".apk")
158-
}.map {
159-
uploadApkToGcsAsync(it.entry, it.bytes)
160-
}.toList()
161-
uploads.awaitAll()
162-
}
163-
}
164-
165-
private fun CoroutineScope.uploadApkToGcsAsync(
166-
zipEntry: ZipEntry,
167-
bytes: ByteArray,
168-
) = async {
169-
apkStore.uploadApk(
170-
name = zipEntry.name,
171-
bytes = bytes
172-
)
173-
}
174-
175147
companion object {
176148
internal const val RESULT_JSON_FILE_NAME = "result.json"
177149
fun create(
@@ -187,6 +159,8 @@ class TestRunner internal constructor(
187159
bucketPath: String,
188160
devicePicker: DevicePicker? = null,
189161
artifactNameFilter: (String) -> Boolean = { true },
162+
useTestConfigFiles: Boolean,
163+
testSuiteTags: List<String>
190164
): TestRunner {
191165
val credentials = ServiceAccountCredentials.fromStream(
192166
googleCloudCredentials.byteInputStream(Charsets.UTF_8)
@@ -235,7 +209,8 @@ class TestRunner internal constructor(
235209
outputFolder = outputFolder,
236210
targetRunId = targetRunId,
237211
hostRunId = hostRunId,
238-
devicePicker = devicePicker
212+
devicePicker = devicePicker,
213+
testSchedulerFactory = TestScheduler.createFactory(useTestConfigFiles, testSuiteTags)
239214
)
240215
}
241216

0 commit comments

Comments
 (0)