@@ -174,22 +174,23 @@ internal class TestRunnerServiceImpl internal constructor(
174174 } else if (fileName.endsWith(LOGCAT_FILE_NAME_SUFFIX )) {
175175 val step = steps.flatMap {
176176 it.testExecutionStep?.toolExecution?.toolOutputs ? : emptyList()
177- }? .find {
177+ }.find {
178178 (it.output?.fileUri == visitor.gcsPath.toString())
179179 }
180180 val runNumber = DeviceRun .create(visitor.fullDeviceId()).runNumber
181181 step?.testCase?.className?.let { className ->
182- step? .testCase? .name?.let { name ->
182+ step.testCase.name?.let { name ->
183183 TestRunnerService .TestIdentifier (
184184 className,
185185 name,
186186 runNumber
187187 )
188188 }
189189 }?.let { testIdentifier ->
190- getTestResultFiles(visitor).addTestCaseLogcat (
190+ getTestResultFiles(visitor).addTestCaseArtifact (
191191 testIdentifier,
192- ResultFileResourceImpl (visitor)
192+ ResultFileResourceImpl (visitor),
193+ " logcat"
193194 )
194195 }
195196 }
@@ -206,13 +207,57 @@ internal class TestRunnerServiceImpl internal constructor(
206207 }
207208 }
208209
210+ private suspend fun findScreenshotFiles (
211+ resultPath : GcsPath ,
212+ testIdentifiers : List <TestRunnerService .TestIdentifier >
213+ ): Map < TestRunnerService .TestIdentifier , List<TestRunnerService.TestCaseArtifact>> {
214+ val screenshotArtifactsBlobs = mutableMapOf<TestRunnerService .TestIdentifier , MutableList <TestRunnerService .TestCaseArtifact >>()
215+ val screenshotArtifacts: Map <TestRunnerService .TestIdentifier , List <TestRunnerService .TestCaseArtifact >>
216+ val testNames = testIdentifiers.associateBy { testIdentifier ->
217+ " ${testIdentifier.className} _${testIdentifier.name} "
218+ }
219+ val testRunNumber = testIdentifiers.first().runNumber
220+ fun BlobVisitor.fullDeviceId () = relativePath.substringBefore(' /' , " " )
221+ googleCloudApi.walkEntires(
222+ gcsPath = resultPath
223+ ).forEach { visitor ->
224+ val runNumber = DeviceRun .create(visitor.fullDeviceId()).runNumber
225+ if (runNumber == testRunNumber) {
226+ val testName = testNames.keys.find { testName ->
227+ visitor.fileName.startsWith(testName)
228+ }
229+ val testIdentifier = testNames[testName]
230+ if (testIdentifier != null ) {
231+ screenshotArtifactsBlobs.getOrPut(testIdentifier) {
232+ mutableListOf ()
233+ }.add(
234+ TestRunnerService .TestCaseArtifact (
235+ ResultFileResourceImpl (visitor),
236+ visitor.fileName.substringAfterLast(" ." )
237+ )
238+ )
239+ }
240+ }
241+ }
242+ screenshotArtifacts = screenshotArtifactsBlobs
243+ return screenshotArtifacts
244+ }
245+
209246 suspend fun getTestMatrixResults (
210247 testMatrixId : String
211248 ): List <TestRunnerService .TestRunResult >? {
212249 val testMatrix = testLabController.getTestMatrix(testMatrixId) ? : return null
213250 return getTestMatrixResults(testMatrix)
214251 }
215252
253+ suspend fun getTestMatrixResultsScreenshots (
254+ testMatrixId : String ,
255+ testIdentifiers : List <TestRunnerService .TestIdentifier >
256+ ): Map <TestRunnerService .TestIdentifier , List <TestRunnerService .TestCaseArtifact >>? {
257+ val testMatrix = testLabController.getTestMatrix(testMatrixId) ? : return null
258+ return getTestMatrixResultsScreenshots(testMatrix, testIdentifiers)
259+ }
260+
216261 override suspend fun getTestMatrixResults (
217262 testMatrix : TestMatrix
218263 ): List <TestRunnerService .TestRunResult >? {
@@ -221,6 +266,15 @@ internal class TestRunnerServiceImpl internal constructor(
221266 return findResultFiles(resultPath, testMatrix)
222267 }
223268
269+ override suspend fun getTestMatrixResultsScreenshots (
270+ testMatrix : TestMatrix ,
271+ testIdentifiers : List <TestRunnerService .TestIdentifier >
272+ ): Map <TestRunnerService .TestIdentifier , List <TestRunnerService .TestCaseArtifact >>? {
273+ if (! testMatrix.isComplete()) return null
274+ val resultPath = GcsPath (testMatrix.resultStorage.googleCloudStorage.gcsPath)
275+ return findScreenshotFiles(resultPath, testIdentifiers)
276+ }
277+
224278 companion object {
225279 private const val MERGED_TEST_RESULT_SUFFIX = " -test_results_merged.xml"
226280 private const val LOGCAT_FILE_NAME = " logcat"
@@ -248,23 +302,34 @@ internal class TestRunnerServiceImpl internal constructor(
248302 fullDeviceId : String ,
249303 ) : TestRunnerService.TestResultFiles {
250304 private val xmlResultBlobs = mutableListOf<TestRunnerService .ResultFileResource >()
251- private val testCaseLogcatBlobs = mutableMapOf<TestRunnerService .TestIdentifier , TestRunnerService .ResultFileResource >()
305+ private val testCaseArtifactBlobs = mutableMapOf<TestRunnerService .TestIdentifier , MutableList < TestRunnerService .TestCaseArtifact > >()
252306
253307 override var logcat: TestRunnerService .ResultFileResource ? = null
254308 internal set
255309 override var instrumentationResult: TestRunnerService .ResultFileResource ? = null
256310 internal set
257311 override val xmlResults: List <TestRunnerService .ResultFileResource > = xmlResultBlobs
258- override val testCaseLogcats : Map <TestRunnerService .TestIdentifier , TestRunnerService .ResultFileResource > = testCaseLogcatBlobs
312+ override val testCaseArtifacts : Map <TestRunnerService .TestIdentifier , List < TestRunnerService .TestCaseArtifact >> = testCaseArtifactBlobs
259313 override val deviceRun: DeviceRun = DeviceRun .create(fullDeviceId)
260314
261315 internal fun addXmlResult (resultFileResource : TestRunnerService .ResultFileResource ) {
262316 xmlResultBlobs.add(resultFileResource)
263317 }
264- internal fun addTestCaseLogcat (testCase : TestRunnerService .TestIdentifier , resultFileResource : TestRunnerService .ResultFileResource ) {
265- testCaseLogcatBlobs[testCase] = resultFileResource
266- }
267318
319+ internal fun addTestCaseArtifact (
320+ testCase : TestRunnerService .TestIdentifier ,
321+ resultFileResource : TestRunnerService .ResultFileResource ,
322+ resourceType : String
323+ ) {
324+ testCaseArtifactBlobs.getOrPut(testCase) {
325+ mutableListOf ()
326+ }.add(
327+ TestRunnerService .TestCaseArtifact (
328+ resultFileResource,
329+ resourceType
330+ )
331+ )
332+ }
268333 override fun toString (): String {
269334 return """
270335 TestResultFiles(
0 commit comments