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(ios): extra artifacts #756

Draft
wants to merge 1 commit into
base: develop
Choose a base branch
from
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,10 @@ class ConfigurationFactory(
val resolvedDerivedDataDir = it.derivedDataDir?.let { ddd -> marathonfileDir.resolve(ddd) }
val resolvedApplication = it.application?.let { ddd -> marathonfileDir.resolve(ddd) }
val resolvedTestApplication = it.testApplication?.let { ddd -> marathonfileDir.resolve(ddd) }
val resolvedExtraArtifacts = it.extraArtifacts?.map { ddd -> marathonfileDir.resolve(ddd) }
val resolvedExtraApplications = it.extraApplications?.map { ddd -> marathonfileDir.resolve(ddd) }
AppleTestBundleConfiguration(resolvedApplication, resolvedTestApplication, resolvedExtraApplications, resolvedDerivedDataDir).apply { validate() }

AppleTestBundleConfiguration(resolvedApplication, resolvedTestApplication, resolvedExtraApplications, resolvedExtraArtifacts, resolvedDerivedDataDir).apply { validate() }
}
val optionalDevices = configuration.vendorConfiguration.devicesFile?.resolveAgainst(marathonfileDir)
?: marathonfileDir.resolve("Marathondevices")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ data class AppleTestBundleConfiguration(
@JsonProperty("application") val application: File? = null,
@JsonProperty("testApplication") val testApplication: File? = null,
@JsonProperty("extraApplications") val extraApplications: List<File>? = null,
@JsonProperty("extraArtifacts") val extraArtifacts: List<File>? = null,
@JsonProperty("derivedDataDir") val derivedDataDir: File? = null,
@JsonProperty("testType") val testType: TestType? = null,
private val tempDirFor: (File) -> File = { file ->
Expand Down
18 changes: 18 additions & 0 deletions docs/docs/ios/configure.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,24 @@ extraApplications:
- "/path/to/additional.app"
```

#### Extra artifacts
Marathon can push additional artifacts such as files and folders to the remote environment for testing:

```yaml
extraArtifacts:
- "/path/to/my/folder"
```

To retrieve these artifacts from the test marathon passes an environment variable `TEST_EXTRA_ARTIFACTS` with the absolute path of the
folder containing all of the `extraArtifacts`, e.g. for the example above `$TEST_EXTRA_ARTIFACTS/folder` will be a valid path to read.

:::tip

There is no unwrapping during the push process so a local folder `a` will be a remote folder `TEST_EXTRA_ARTIFACTS/a` and
a local file `b` will be a remote file `TEST_EXTRA_ARTIFACTS/b`.

:::

### Devices
By default, marathon will look for a file `Marathondevices` in the same folder as `Marathonfile` for the configuration of workers. You can
override this location with the following property:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class AppleApplicationInstaller(
logger.debug { "Moving xctest to ${device.serialNumber}" }
val remoteXctest = device.remoteFileManager.remoteXctestFile()
withRetry(3, 1000L) {
device.remoteFileManager.createRemoteDirectory()
device.remoteFileManager.createRemoteDirectories()
val remoteDirectory = device.remoteFileManager.remoteDirectory()
if (!device.pushFolder(xctest, remoteXctest)) {
throw DeviceSetupException("Error transferring $xctest to ${device.serialNumber}")
Expand Down Expand Up @@ -57,6 +57,16 @@ class AppleApplicationInstaller(
logger.warn { "Extra application $it should be a directory with extension app" }
}
}

bundle.extraArtifacts?.forEach {
logger.debug { "Pushing extra artifact $it to ${device.serialNumber}" }
val remoteArtifactFile = device.remoteFileManager.remoteArtifactFile(it.name)
if (it.isDirectory) {
device.pushFolder(it, remoteArtifactFile)
} else {
device.pushFile(it, remoteArtifactFile)
}
}
}

private suspend fun grantPermissions(device: AppleSimulatorDevice) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ class AppleSimulatorDevice(
}
override val coroutineContext: CoroutineContext = dispatcher
override val remoteFileManager: RemoteFileManager = RemoteFileManager(this)
override val storagePath = "/tmp/marathon/$udid"
override val storagePath = "${RemoteFileManager.MARATHON_ROOT_PATH}/$udid"
private lateinit var xcodeVersion: XcodeVersion

/**
Expand Down Expand Up @@ -188,8 +188,8 @@ class AppleSimulatorDevice(
async(CoroutineName("prepare $serialNumber")) {
supervisorScope {
track.trackDevicePreparing(this@AppleSimulatorDevice) {
remoteFileManager.removeRemoteDirectory()
remoteFileManager.createRemoteDirectory()
remoteFileManager.removeRemoteDirectories()
remoteFileManager.createRemoteDirectories()
//Clean slate for the recorder
executeWorkerCommand(listOf("pkill", "-f", "'simctl io ${udid} recordVideo'"))
mutableListOf<Deferred<Unit>>().apply {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import com.malinskiy.marathon.config.Configuration
import com.malinskiy.marathon.config.exceptions.ConfigurationException
import com.malinskiy.marathon.config.vendor.VendorConfiguration
import com.malinskiy.marathon.device.Device
import com.malinskiy.marathon.device.DeviceProvider
import com.malinskiy.marathon.exceptions.TestParsingException
import com.malinskiy.marathon.execution.RemoteTestParser
import com.malinskiy.marathon.execution.withRetry
Expand Down Expand Up @@ -56,7 +55,7 @@ class AppleTestParser(

logger.debug { "Found test binary $testBinary for xctest $xctest" }

device.remoteFileManager.createRemoteDirectory()
device.remoteFileManager.createRemoteDirectories()
val remoteXctest = device.remoteFileManager.remoteXctestFile()
if (!device.pushFile(xctest, remoteXctest)) {
throw TestParsingException("failed to push xctest for test parsing")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,23 @@ class RemoteFileManager(private val device: AppleDevice) {
private val outputDir by lazy { device.storagePath }

fun remoteDirectory(): String = outputDir
fun remoteArtifactDirectory(): String = MARATHON_ROOT_PATH.resolve("extraArtifacts")

suspend fun createRemoteDirectory(remoteDir: String = outputDir) {
suspend fun createRemoteDirectories() {
createRemoteDirectory(remoteDirectory())
createRemoteDirectory(remoteArtifactDirectory())
}

suspend fun createRemoteDirectory(path: String) {
executeCommand(
listOf("mkdir", "-p", remoteDirectory()),
"Could not create remote directory ${remoteDirectory()}"
listOf("mkdir", "-p", path),
"Could not create remote directory $path"
)
}

suspend fun removeRemoteDirectory() {
suspend fun removeRemoteDirectories() {
executeCommand(
listOf("rm", "-rf", remoteDirectory()),
listOf("rm", "-rf", MARATHON_ROOT_PATH),
"Unable to remove directory ${remoteDirectory()}"
)
}
Expand All @@ -43,6 +49,7 @@ class RemoteFileManager(private val device: AppleDevice) {
fun remoteXctestFile(): String = remoteFile(xctestFileName())
fun remoteApplication(): String = remoteFile(appUnderTestFileName())
fun remoteExtraApplication(name: String) = remoteFile(name)
fun remoteExtraArtifact(name: String) = remoteFile(name)

/**
* Omitting xcresult extension results in a symlink
Expand All @@ -58,6 +65,7 @@ class RemoteFileManager(private val device: AppleDevice) {
"${device.udid}.${batch.id}.xcresult"

private fun remoteFile(file: String): String = remoteDirectory().resolve(file)
fun remoteArtifactFile(file: String): String = remoteArtifactDirectory().resolve(file)

private suspend fun safeExecuteCommand(command: List<String>) {
try {
Expand Down Expand Up @@ -132,6 +140,7 @@ class RemoteFileManager(private val device: AppleDevice) {

companion object {
const val FILE_SEPARATOR = "/"
const val MARATHON_ROOT_PATH = "/tmp/marathon"
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ class TestRootFactory(private val device: AppleSimulatorDevice, private val vend
val remoteFileManager = device.remoteFileManager

val testRoot = remoteFileManager.remoteTestRoot()
remoteFileManager.createRemoteDirectory(testRoot)
val xctestrun = when (testType) {
TestType.XCUITEST -> generateXCUITest(testRoot, remoteFileManager, bundleConfiguration)
TestType.XCTEST -> generateXCTest(testRoot, remoteFileManager, bundleConfiguration)
Expand Down Expand Up @@ -102,6 +101,7 @@ class TestRootFactory(private val device: AppleSimulatorDevice, private val vend
.forEach {
put(it.key, it.value)
}
put(ENV_TEST_EXTRA_ARTIFACTS, remoteFileManager.remoteArtifactDirectory())
}.toMap()

return Xctestrun(
Expand Down Expand Up @@ -204,6 +204,7 @@ class TestRootFactory(private val device: AppleSimulatorDevice, private val vend
.forEach {
put(it.key, it.value)
}
put(ENV_TEST_EXTRA_ARTIFACTS, remoteFileManager.remoteArtifactDirectory())
}.toMap()

return Xctestrun(
Expand Down Expand Up @@ -273,4 +274,8 @@ class TestRootFactory(private val device: AppleSimulatorDevice, private val vend
}
}
}

companion object {
const val ENV_TEST_EXTRA_ARTIFACTS = "TEST_EXTRA_ARTIFACTS"
}
}