Skip to content

Commit

Permalink
refactor: data scratch ios (#1952)
Browse files Browse the repository at this point in the history
Fixes #1755 

## Test Plan
> How do we know the code works?

- Code is refactored according to description provided by #1755
- It takes into account work done on: #1941 and attempts to be as close as possible for consistency.

## Checklist

- [x] Unit tested
- [x] Integration tests updated
  • Loading branch information
Sloox authored May 25, 2021
1 parent b35309f commit 19fac80
Show file tree
Hide file tree
Showing 12 changed files with 271 additions and 159 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class AllTestFilteredIT {

assertNoOutcomeSummary(resOutput)

assertThat(outputReport.error).contains("There are no tests to run.")
assertThat(outputReport.error).contains("There are no Android tests to run.")
assertThat(outputReport.cost).isNull()
assertThat(outputReport.testResults).isEmpty()
assertThat(outputReport.weblinks).isEmpty()
Expand Down
16 changes: 16 additions & 0 deletions test_runner/src/main/kotlin/ftl/adapter/GoogleTestMatrixIos.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package ftl.adapter

import ftl.adapter.google.toApiModel
import ftl.api.TestMatrix
import ftl.api.TestMatrixIos
import ftl.client.google.run.ios.executeIosTests
import kotlinx.coroutines.runBlocking
import com.google.testing.model.TestMatrix as GoogleTestMatrix

object GoogleTestMatrixIos :
TestMatrixIos.Execute,
(TestMatrixIos.Config, List<TestMatrixIos.Type>) -> List<TestMatrix.Data> by { config, types ->
runBlocking {
executeIosTests(config, types).map(GoogleTestMatrix::toApiModel)
}
}
45 changes: 45 additions & 0 deletions test_runner/src/main/kotlin/ftl/api/TestMatrixIOS.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package ftl.api

import ftl.adapter.GoogleTestMatrixIos
import ftl.config.Device

val executeTestMatrixIos: TestMatrixIos.Execute get() = GoogleTestMatrixIos

object TestMatrixIos {

data class Config(
// args
val clientDetails: Map<String, String>?,
val networkProfile: String?,
val directoriesToPull: List<String>,
val testTimeout: String,
val recordVideo: Boolean,
val flakyTestAttempts: Int,
val failFast: Boolean,
val project: String,
val resultsHistoryName: String?,

// build
val devices: List<Device>,
val otherFiles: Map<String, String>,
val additionalIpasGcsPaths: List<String>,
)

sealed class Type(open val matrixGcsPath: String) {
data class XcTest(
val xcTestGcsPath: String,
val xcTestRunFileGcsPath: String,
val xcodeVersion: String,
val testSpecialEntitlements: Boolean,
override val matrixGcsPath: String,
) : Type(matrixGcsPath)

data class GameLoop(
val appGcsPath: String,
val scenarios: List<Int>,
override val matrixGcsPath: String,
) : Type(matrixGcsPath)
}

interface Execute : (Config, List<Type>) -> List<TestMatrix.Data>
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package ftl.gc
package ftl.client.google.run.ios

import com.google.testing.model.IosDevice
import com.google.testing.model.IosDeviceList
import ftl.config.Device

object GcIosMatrix {
object GcIosDevice {

fun build(deviceList: List<Device>): IosDeviceList = IosDeviceList().setIosDevices(
deviceList.map {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package ftl.client.google.run.ios

import com.google.testing.Testing
import com.google.testing.model.ClientInfo
import com.google.testing.model.EnvironmentMatrix
import com.google.testing.model.GoogleCloudStorage
import com.google.testing.model.IosTestSetup
import com.google.testing.model.ResultStorage
import com.google.testing.model.TestMatrix
import com.google.testing.model.TestSpecification
import com.google.testing.model.ToolResultsHistory
import ftl.api.TestMatrixIos
import ftl.client.google.GcTesting
import ftl.client.google.run.mapGcsPathsToFileReference
import ftl.client.google.run.mapToIosDeviceFiles
import ftl.client.google.run.toClientInfoDetailList
import ftl.client.google.run.toIosDeviceFile
import ftl.http.executeWithRetry
import ftl.run.exception.FlankGeneralError
import ftl.util.timeoutToSeconds
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope

suspend fun executeIosTests(
config: TestMatrixIos.Config,
testMatrixTypes: List<TestMatrixIos.Type>,
): List<TestMatrix> = testMatrixTypes
.foldIndexed(emptyList<Deferred<TestMatrix>>()) { _, testMatrices, testMatrixType ->
testMatrices + executeIosTestMatrixAsync(testMatrixType, config)
}.awaitAll()

private suspend fun executeIosTestMatrixAsync(
type: TestMatrixIos.Type,
config: TestMatrixIos.Config
): Deferred<TestMatrix> = coroutineScope {
async(Dispatchers.IO) {
createIosTestMatrix(type, config).executeWithRetry()
}
}

private fun createIosTestMatrix(
testMatrixType: TestMatrixIos.Type,
config: TestMatrixIos.Config,
): Testing.Projects.TestMatrices.Create {

val testMatrix = TestMatrix()
.setClientInfo(config.clientInfo)
.setTestSpecification(getIosTestSpecification(testMatrixType, config))
.setEnvironmentMatrix(config.environmentMatrix)
.setResultStorage(resultsStorage(config, testMatrixType))
.setFlakyTestAttempts(config.flakyTestAttempts)
.setFailFast(config.failFast)

return runCatching {
GcTesting.get.projects().testMatrices().create(config.project, testMatrix)
}.getOrElse { throw FlankGeneralError(it) }
}

private fun resultsStorage(config: TestMatrixIos.Config, type: TestMatrixIos.Type): ResultStorage {
return ResultStorage().setGoogleCloudStorage(
GoogleCloudStorage().setGcsPath(type.matrixGcsPath)
).setToolResultsHistory(
ToolResultsHistory().setHistoryId(config.resultsHistoryName).setProjectId(config.project)
)
}

private val TestMatrixIos.Config.environmentMatrix
get() = EnvironmentMatrix().setIosDeviceList(GcIosDevice.build(devices))

private fun getIosTestSpecification(
testMatrixType: TestMatrixIos.Type,
config: TestMatrixIos.Config
): TestSpecification = TestSpecification()
.setDisableVideoRecording(!config.recordVideo)
.setTestTimeout("${timeoutToSeconds(config.testTimeout)}s")
.setIosTestSetup(getIosTestSetup(config))
.setupIosTest(testMatrixType)

private fun getIosTestSetup(
config: TestMatrixIos.Config
): IosTestSetup = IosTestSetup()
.setNetworkProfile(config.networkProfile)
.setPushFiles(config.otherFiles.mapToIosDeviceFiles())
.setAdditionalIpas(config.additionalIpasGcsPaths.mapGcsPathsToFileReference())
.setPullDirectories(config.directoriesToPull.toIosDeviceFiles())

private fun List<String>.toIosDeviceFiles() = map { path -> toIosDeviceFile(path) }

private val TestMatrixIos.Config.clientInfo
get() = ClientInfo().setName("Flank").setClientInfoDetails(clientDetails?.toClientInfoDetailList())
Original file line number Diff line number Diff line change
@@ -1,28 +1,26 @@
package ftl.gc.ios
package ftl.client.google.run.ios

import com.google.testing.model.FileReference
import com.google.testing.model.IosTestLoop
import com.google.testing.model.IosXcTest
import com.google.testing.model.TestSpecification
import ftl.run.model.GameloopTestContext
import ftl.run.model.IosTestContext
import ftl.run.model.XcTestContext
import ftl.api.TestMatrixIos

fun TestSpecification.setupIosTest(context: IosTestContext) = apply {
when (context) {
is XcTestContext -> iosXcTest = setupXcTest(context)
is GameloopTestContext -> iosTestLoop = setupGameLoopTest(context)
internal fun TestSpecification.setupIosTest(config: TestMatrixIos.Type) = apply {
when (config) {
is TestMatrixIos.Type.XcTest -> iosXcTest = setupXcTest(config)
is TestMatrixIos.Type.GameLoop -> iosTestLoop = setupGameLoopTest(config)
}
}

private fun setupXcTest(context: XcTestContext) = IosXcTest().apply {
private fun setupXcTest(context: TestMatrixIos.Type.XcTest) = IosXcTest().apply {
testsZip = FileReference().setGcsPath(context.xcTestGcsPath)
xctestrun = FileReference().setGcsPath(context.xcTestRunFileGcsPath)
xcodeVersion = context.xcodeVersion
testSpecialEntitlements = context.testSpecialEntitlements
}

private fun setupGameLoopTest(context: GameloopTestContext) = IosTestLoop().apply {
private fun setupGameLoopTest(context: TestMatrixIos.Type.GameLoop) = IosTestLoop().apply {
appIpa = FileReference().setGcsPath(context.appGcsPath)
scenarios = context.scenarios
}
75 changes: 0 additions & 75 deletions test_runner/src/main/kotlin/ftl/gc/GcIosTestMatrix.kt

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ internal suspend fun AndroidArgs.runAndroidTests(): TestResult = coroutineScope
.map { context -> createAndroidTestMatrixType(context) }
.run { executeTestMatrixAndroid(createAndroidTestConfig(args), toList()) }
.takeIf { it.isNotEmpty() }
?: throw FlankGeneralError("There are no tests to run.")
?: throw FlankGeneralError("There are no Android tests to run.")

logLn(beforeRunMessage(allTestShardChunks))

Expand Down
37 changes: 9 additions & 28 deletions test_runner/src/main/kotlin/ftl/run/platform/RunIosTests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,21 @@ import ftl.api.uploadToRemoteStorage
import ftl.args.IosArgs
import ftl.args.isXcTest
import ftl.args.shardsFilePath
import ftl.client.google.GcToolResults
import ftl.client.google.run.ios.executeIosTests
import ftl.config.FtlConstants
import ftl.gc.GcIosMatrix
import ftl.gc.GcIosTestMatrix
import ftl.http.executeWithRetry
import ftl.ios.xctest.flattenShardChunks
import ftl.run.dumpShards
import ftl.run.exception.FlankGeneralError
import ftl.run.model.TestResult
import ftl.run.platform.android.uploadAdditionalIpas
import ftl.run.platform.android.uploadOtherFiles
import ftl.run.platform.common.afterRunTests
import ftl.run.platform.common.beforeRunMessage
import ftl.run.platform.common.beforeRunTests
import ftl.run.platform.ios.createIosTestConfig
import ftl.run.platform.ios.createIosTestContexts
import ftl.run.platform.ios.createIosTestMatrixType
import ftl.shard.testCases
import ftl.util.repeat
import ftl.util.saveToFlankLinks
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.toList
Expand All @@ -41,12 +36,6 @@ internal suspend fun IosArgs.runIosTests(): TestResult = coroutineScope {
val args = this@runIosTests
val stopwatch = beforeRunTests()

val iosDeviceList = GcIosMatrix.build(devices)

val history = GcToolResults.createToolResultsHistory(args)
val otherGcsFiles = uploadOtherFiles()
val additionalIpasGcsFiles = uploadAdditionalIpas()

dumpShardsIfXcTest()
saveToFlankLinks(
shardsFilePath,
Expand All @@ -55,19 +44,11 @@ internal suspend fun IosArgs.runIosTests(): TestResult = coroutineScope {
val testShardChunks = xcTestRunData.flattenShardChunks()
logLn(beforeRunMessage(testShardChunks))

val result = createIosTestContexts().map { context ->
GcIosTestMatrix.build(
iosDeviceList = iosDeviceList,
args = args,
iosTestContext = context,
toolResultsHistory = history,
otherFiles = otherGcsFiles,
additionalIpasGcsPaths = additionalIpasGcsFiles
)
}.repeat(repeatTests)
.map { async(Dispatchers.IO) { it.executeWithRetry() } }
.toList()
.awaitAll()
val result = createIosTestContexts()
.map { context -> createIosTestMatrixType(context) }
.repeat(repeatTests)
.run { executeIosTests(createIosTestConfig(args), toList()) }
.takeIf { it.isNotEmpty() } ?: throw FlankGeneralError("There are no iOS tests to run")

TestResult(
matrixMap = afterRunTests(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package ftl.run.platform.ios

import ftl.api.TestMatrixIos
import ftl.args.IosArgs
import ftl.client.google.GcToolResults
import ftl.run.platform.android.uploadAdditionalIpas
import ftl.run.platform.android.uploadOtherFiles

suspend fun createIosTestConfig(
args: IosArgs
): TestMatrixIos.Config = TestMatrixIos.Config(
clientDetails = args.clientDetails,
networkProfile = args.networkProfile,
directoriesToPull = args.directoriesToPull,
testTimeout = args.testTimeout,
recordVideo = args.recordVideo,
flakyTestAttempts = args.flakyTestAttempts,
failFast = args.failFast,
project = args.project,
resultsHistoryName = GcToolResults.createToolResultsHistory(args).historyId,
devices = args.devices,
otherFiles = args.uploadOtherFiles(),
additionalIpasGcsPaths = args.uploadAdditionalIpas(),
)
Loading

0 comments on commit 19fac80

Please sign in to comment.