diff --git a/configuration/src/main/kotlin/com/malinskiy/marathon/config/vendor/VendorConfiguration.kt b/configuration/src/main/kotlin/com/malinskiy/marathon/config/vendor/VendorConfiguration.kt index 946d92f1e..d40e2c048 100644 --- a/configuration/src/main/kotlin/com/malinskiy/marathon/config/vendor/VendorConfiguration.kt +++ b/configuration/src/main/kotlin/com/malinskiy/marathon/config/vendor/VendorConfiguration.kt @@ -162,6 +162,7 @@ sealed class VendorConfiguration { @JsonProperty("compactOutput") val compactOutput: Boolean = false, @JsonProperty("rsync") val rsync: RsyncConfiguration = RsyncConfiguration(), @JsonProperty("xcodebuildTestArgs") val xcodebuildTestArgs: Map = emptyMap(), + @JsonProperty("dataContainerClear") val dataContainerClear: Boolean = false, @JsonProperty("testParserConfiguration") val testParserConfiguration: com.malinskiy.marathon.config.vendor.ios.TestParserConfiguration = com.malinskiy.marathon.config.vendor.ios.TestParserConfiguration.NmTestParserConfiguration(), @JsonProperty("signing") val signing: SigningConfiguration = SigningConfiguration(), diff --git a/docs/docs/ios/configure.md b/docs/docs/ios/configure.md index 179cf8977..60d9f41b4 100644 --- a/docs/docs/ios/configure.md +++ b/docs/docs/ios/configure.md @@ -357,7 +357,7 @@ timeoutConfiguration: screenshot: PT10S video: PT300S erase: PT30S - shutdown: PT30S + shutdown: PT30S delete: PT30S create: PT30S boot: PT30S @@ -454,6 +454,15 @@ rsync: remotePath: "/usr/bin/rsync-custom" ``` +### Clear state between test batch executions +By default, marathon does not clear state between test batch executions. To mitigate potential test side effects, one could add an option to +clear the container data between test runs. Keep in mind that test side effects might still be present. +If you want to isolate tests even further, then you should consider reducing the batch size. + +```yaml +dataContainerClear: true +``` + ### Test parser :::tip diff --git a/vendor/vendor-ios/src/main/kotlin/com/malinskiy/marathon/ios/AppleApplicationInstaller.kt b/vendor/vendor-ios/src/main/kotlin/com/malinskiy/marathon/ios/AppleApplicationInstaller.kt index dc1e3ddac..68f24fd7f 100644 --- a/vendor/vendor-ios/src/main/kotlin/com/malinskiy/marathon/ios/AppleApplicationInstaller.kt +++ b/vendor/vendor-ios/src/main/kotlin/com/malinskiy/marathon/ios/AppleApplicationInstaller.kt @@ -6,6 +6,7 @@ import com.malinskiy.marathon.config.vendor.VendorConfiguration import com.malinskiy.marathon.config.vendor.ios.TestType import com.malinskiy.marathon.exceptions.DeviceSetupException import com.malinskiy.marathon.execution.withRetry +import com.malinskiy.marathon.ios.extensions.testBundle import com.malinskiy.marathon.ios.model.Sdk import com.malinskiy.marathon.ios.xctestrun.TestRootFactory import com.malinskiy.marathon.log.MarathonLogging @@ -18,9 +19,9 @@ class AppleApplicationInstaller( private val logger = MarathonLogging.logger {} suspend fun prepareInstallation(device: AppleSimulatorDevice, useXctestParser: Boolean = false) { - val bundle = vendorConfiguration.bundle ?: throw ConfigurationException("no xctest found for configuration") + val bundle = vendorConfiguration.testBundle() ?: throw ConfigurationException("no xctest found for configuration") - val xctest = bundle.xctest + val xctest = bundle.testApplication logger.debug { "Moving xctest to ${device.serialNumber}" } val remoteXctest = device.remoteFileManager.remoteXctestFile() withRetry(3, 1000L) { @@ -46,7 +47,7 @@ class AppleApplicationInstaller( TestRootFactory(device, vendorConfiguration).generate(testType, bundle, useXctestParser) grantPermissions(device) - bundle.extraApplications?.forEach { + vendorConfiguration.bundle?.extraApplications?.forEach { if (it.isDirectory && it.extension == "app") { logger.debug { "Installing extra application $it to ${device.serialNumber}" } val remoteExtraApplication = device.remoteFileManager.remoteExtraApplication(it.name) diff --git a/vendor/vendor-ios/src/main/kotlin/com/malinskiy/marathon/ios/AppleDeviceTestRunner.kt b/vendor/vendor-ios/src/main/kotlin/com/malinskiy/marathon/ios/AppleDeviceTestRunner.kt index 2b7a67703..e2fe955f9 100644 --- a/vendor/vendor-ios/src/main/kotlin/com/malinskiy/marathon/ios/AppleDeviceTestRunner.kt +++ b/vendor/vendor-ios/src/main/kotlin/com/malinskiy/marathon/ios/AppleDeviceTestRunner.kt @@ -18,7 +18,7 @@ import com.malinskiy.marathon.test.toTestName import kotlinx.coroutines.CancellationException import kotlinx.coroutines.channels.ReceiveChannel -class AppleDeviceTestRunner(private val device: AppleSimulatorDevice) { +class AppleDeviceTestRunner(private val device: AppleSimulatorDevice, private val bundleIdentifier: AppleTestBundleIdentifier) { private val logger = MarathonLogging.logger {} suspend fun execute( @@ -43,7 +43,15 @@ class AppleDeviceTestRunner(private val device: AppleSimulatorDevice) { ) var channel: ReceiveChannel>? = null try { - clearData(vendorConfiguration) + if (vendorConfiguration.dataContainerClear) { + val bundleIds = rawTestBatch.tests.map { + bundleIdentifier.identify(it).appId + }.toSet() + bundleIds.forEach { + device.clearAppContainer(it) + } + } + listener.beforeTestRun() val localChannel = device.executeTestRequest(runnerRequest) @@ -77,11 +85,4 @@ class AppleDeviceTestRunner(private val device: AppleSimulatorDevice) { } } } - - private suspend fun clearData(vendorConfiguration: VendorConfiguration.IOSConfiguration) { -// if (vendorConfiguration.eraseSimulatorOnStart) { -// device.shutdown() -// device.erase() -// } - } } diff --git a/vendor/vendor-ios/src/main/kotlin/com/malinskiy/marathon/ios/AppleSimulatorDevice.kt b/vendor/vendor-ios/src/main/kotlin/com/malinskiy/marathon/ios/AppleSimulatorDevice.kt index bf839f7d8..4b09fb293 100644 --- a/vendor/vendor-ios/src/main/kotlin/com/malinskiy/marathon/ios/AppleSimulatorDevice.kt +++ b/vendor/vendor-ios/src/main/kotlin/com/malinskiy/marathon/ios/AppleSimulatorDevice.kt @@ -22,6 +22,7 @@ import com.malinskiy.marathon.execution.listener.LineListener import com.malinskiy.marathon.execution.listener.LogListener import com.malinskiy.marathon.io.FileManager import com.malinskiy.marathon.ios.bin.AppleBinaryEnvironment +import com.malinskiy.marathon.ios.bin.xcrun.simctl.service.ApplicationService import com.malinskiy.marathon.ios.cmd.CommandExecutor import com.malinskiy.marathon.ios.cmd.CommandResult import com.malinskiy.marathon.ios.cmd.FileBridge @@ -105,12 +106,13 @@ class AppleSimulatorDevice( val arch: Arch get() = when { sdk == Sdk.IPHONESIMULATOR -> { - when(abi) { + when (abi) { "x86_64" -> Arch.x86_64 "arm64" -> Arch.arm64 else -> Arch.arm64 } } + udid.contains('-') -> Arch.arm64e else -> Arch.arm64 } @@ -126,7 +128,10 @@ class AppleSimulatorDevice( private lateinit var devicePlistPath: String private var deviceDescriptor: Map<*, *>? = null private val dispatcher by lazy { - newFixedThreadPoolContext(vendorConfiguration.threadingConfiguration.deviceThreads, "AppleSimulatorDevice - execution - ${commandExecutor.host.id}") + newFixedThreadPoolContext( + vendorConfiguration.threadingConfiguration.deviceThreads, + "AppleSimulatorDevice - execution - ${commandExecutor.host.id}" + ) } override val coroutineContext: CoroutineContext = dispatcher override val remoteFileManager: RemoteFileManager = RemoteFileManager(this) @@ -245,7 +250,7 @@ class AppleSimulatorDevice( try { val (listener, lineListeners) = createExecutionListeners(devicePoolId, testBatch, deferred) executionLineListeners = lineListeners.onEach { addLineListener(it) } - AppleDeviceTestRunner(this@AppleSimulatorDevice).execute(configuration, vendorConfiguration, testBatch, listener) + AppleDeviceTestRunner(this@AppleSimulatorDevice, testBundleIdentifier).execute(configuration, vendorConfiguration, testBatch, listener) } finally { executionLineListeners.forEach { removeLineListener(it) } } @@ -752,6 +757,17 @@ class AppleSimulatorDevice( return binaryEnvironment.xcrun.simctl.privacy.grant(udid, permission, bundleId).successful } + suspend fun clearAppContainer(bundleId: String) { + binaryEnvironment.xcrun.simctl.application.terminateApplication(udid, bundleId) + + val containerPath = binaryEnvironment.xcrun.simctl.application.containerPath(udid, bundleId, ApplicationService.ContainerType.DATA) + if (containerPath.successful) { + remoteFileManager.removeRemotePath(containerPath.combinedStdout.trim()) + } else { + logger.warn { "Failed to clear app container:\nstdout: ${containerPath.combinedStdout}\nstderr: ${containerPath.combinedStderr}" } + } + } + companion object { const val SHARED_PATH = "/tmp/marathon" } diff --git a/vendor/vendor-ios/src/main/kotlin/com/malinskiy/marathon/ios/NmTestParser.kt b/vendor/vendor-ios/src/main/kotlin/com/malinskiy/marathon/ios/NmTestParser.kt index db4f422b9..f91412931 100644 --- a/vendor/vendor-ios/src/main/kotlin/com/malinskiy/marathon/ios/NmTestParser.kt +++ b/vendor/vendor-ios/src/main/kotlin/com/malinskiy/marathon/ios/NmTestParser.kt @@ -8,6 +8,7 @@ import com.malinskiy.marathon.device.Device import com.malinskiy.marathon.exceptions.TestParsingException import com.malinskiy.marathon.execution.RemoteTestParser import com.malinskiy.marathon.execution.withRetry +import com.malinskiy.marathon.ios.extensions.testBundle import com.malinskiy.marathon.ios.model.AppleTestBundle import com.malinskiy.marathon.log.MarathonLogging import com.malinskiy.marathon.test.Test @@ -23,23 +24,11 @@ class NmTestParser( private val logger = MarathonLogging.logger(NmTestParser::class.java.simpleName) override suspend fun extract(device: Device): List { - val app = vendorConfiguration.bundle?.app ?: throw IllegalArgumentException("No application bundle provided") - val xctest = vendorConfiguration.bundle?.xctest ?: throw IllegalArgumentException("No test bundle provided") - val possibleTestBinaries = xctest.listFiles()?.filter { it.isFile && it.extension == "" } - ?: throw ConfigurationException("missing test binaries in xctest folder at $xctest") - val testBinary = when (possibleTestBinaries.size) { - 0 -> throw ConfigurationException("missing test binaries in xctest folder at $xctest") - 1 -> possibleTestBinaries[0] - else -> { - logger.warn { "Multiple test binaries present in xctest folder" } - possibleTestBinaries.find { it.name == xctest.nameWithoutExtension } ?: possibleTestBinaries.first() - } - } - + val bundle = vendorConfiguration.testBundle() return withRetry(3, 0) { try { val device = device as? AppleSimulatorDevice ?: throw ConfigurationException("Unexpected device type for remote test parsing") - return@withRetry parseTests(device, xctest, testBinary) + return@withRetry parseTests(device, bundle) } catch (e: CancellationException) { throw e } catch (e: Exception) { @@ -51,9 +40,10 @@ class NmTestParser( private suspend fun parseTests( device: AppleSimulatorDevice, - xctest: File, - testBinary: File + bundle: AppleTestBundle, ): List { + val testBinary = bundle.testBinary + val xctest = bundle.testApplication logger.debug { "Found test binary $testBinary for xctest $xctest" } @@ -88,7 +78,7 @@ class NmTestParser( } - val testBundle = AppleTestBundle(vendorConfiguration.bundle?.application, xctest, testBinary) + val testBundle = AppleTestBundle(vendorConfiguration.bundle?.application, xctest) swiftTests.forEach { testBundleIdentifier.put(it, testBundle) } objectiveCTests.forEach { testBundleIdentifier.put(it, testBundle) } diff --git a/vendor/vendor-ios/src/main/kotlin/com/malinskiy/marathon/ios/XCTestParser.kt b/vendor/vendor-ios/src/main/kotlin/com/malinskiy/marathon/ios/XCTestParser.kt index b39c77147..4675d1210 100644 --- a/vendor/vendor-ios/src/main/kotlin/com/malinskiy/marathon/ios/XCTestParser.kt +++ b/vendor/vendor-ios/src/main/kotlin/com/malinskiy/marathon/ios/XCTestParser.kt @@ -105,7 +105,7 @@ class XCTestParser( } } - val testBundle = AppleTestBundle(vendorConfiguration.bundle?.application, xctest, testBinary) + val testBundle = AppleTestBundle(vendorConfiguration.bundle?.application, xctest) val result = tests.toList() result.forEach { testBundleIdentifier.put(it, testBundle) } diff --git a/vendor/vendor-ios/src/main/kotlin/com/malinskiy/marathon/ios/bin/xcrun/simctl/service/ApplicationService.kt b/vendor/vendor-ios/src/main/kotlin/com/malinskiy/marathon/ios/bin/xcrun/simctl/service/ApplicationService.kt index 8ac917a1b..3acf727b8 100644 --- a/vendor/vendor-ios/src/main/kotlin/com/malinskiy/marathon/ios/bin/xcrun/simctl/service/ApplicationService.kt +++ b/vendor/vendor-ios/src/main/kotlin/com/malinskiy/marathon/ios/bin/xcrun/simctl/service/ApplicationService.kt @@ -16,11 +16,24 @@ class ApplicationService (commandExecutor: CommandExecutor, ) } + suspend fun containerPath(udid: String, bundleId: String, containerType: ContainerType): CommandResult { + return criticalExec( + timeout = timeoutConfiguration.shell, + "get_app_container", udid, bundleId, containerType.value + ) + } + + enum class ContainerType(val value: String) { + APPLICATION("app"), + DATA("data"), + GROUPS("groups") + } + /** * Terminates a running application with the given bundle ID on this device */ - suspend fun terminateApplication(udid: String, bundleId: String): CommandResult { - return criticalExec( + suspend fun terminateApplication(udid: String, bundleId: String): CommandResult? { + return safeExecute( timeout = timeoutConfiguration.shell, "terminate", udid, bundleId ) diff --git a/vendor/vendor-ios/src/main/kotlin/com/malinskiy/marathon/ios/extensions/ConfigurationExtensions.kt b/vendor/vendor-ios/src/main/kotlin/com/malinskiy/marathon/ios/extensions/ConfigurationExtensions.kt new file mode 100644 index 000000000..fd15e2ad4 --- /dev/null +++ b/vendor/vendor-ios/src/main/kotlin/com/malinskiy/marathon/ios/extensions/ConfigurationExtensions.kt @@ -0,0 +1,11 @@ +package com.malinskiy.marathon.ios.extensions + +import com.malinskiy.marathon.config.vendor.VendorConfiguration +import com.malinskiy.marathon.ios.model.AppleTestBundle + +fun VendorConfiguration.IOSConfiguration.testBundle(): AppleTestBundle { + val xctest = bundle?.xctest ?: throw IllegalArgumentException("No test bundle provided") + val app = bundle?.app ?: throw IllegalArgumentException("No application bundle provided") + + return AppleTestBundle(app, xctest) +} diff --git a/vendor/vendor-ios/src/main/kotlin/com/malinskiy/marathon/ios/model/AppleTestBundle.kt b/vendor/vendor-ios/src/main/kotlin/com/malinskiy/marathon/ios/model/AppleTestBundle.kt index 8016321e5..713fda3e8 100644 --- a/vendor/vendor-ios/src/main/kotlin/com/malinskiy/marathon/ios/model/AppleTestBundle.kt +++ b/vendor/vendor-ios/src/main/kotlin/com/malinskiy/marathon/ios/model/AppleTestBundle.kt @@ -1,13 +1,47 @@ package com.malinskiy.marathon.ios.model +import com.dd.plist.NSDictionary +import com.malinskiy.marathon.config.exceptions.ConfigurationException import com.malinskiy.marathon.execution.bundle.TestBundle +import com.malinskiy.marathon.ios.plist.PropertyList +import com.malinskiy.marathon.ios.plist.bundle.BundleInfo +import com.malinskiy.marathon.log.MarathonLogging import java.io.File class AppleTestBundle( val application: File?, val testApplication: File, - val testBinary: File, ) : TestBundle() { + private val logger = MarathonLogging.logger {} override val id: String get() = testApplication.absolutePath + + val applicationBundleInfo: BundleInfo? by lazy { + application?.let { + PropertyList.from( + File( + it, + "Info.plist" + ) + ) + } + } + val appId = + applicationBundleInfo?.identification?.bundleIdentifier ?: throw ConfigurationException("No bundle identifier specified in $application") + + val testBundleInfo: BundleInfo by lazy { PropertyList.from(File(testApplication, "Info.plist")) } + val testBundleId = (testBundleInfo.naming.bundleName ?: testApplication.nameWithoutExtension).replace('-', '_') + + val testBinary: File by lazy { + val possibleTestBinaries = testApplication.listFiles()?.filter { it.isFile && it.extension == "" } + ?: throw ConfigurationException("missing test binaries in xctest folder at $testApplication") + when (possibleTestBinaries.size) { + 0 -> throw ConfigurationException("missing test binaries in xctest folder at $testApplication") + 1 -> possibleTestBinaries[0] + else -> { + logger.warn { "Multiple test binaries present in xctest folder" } + possibleTestBinaries.find { it.name == testApplication.nameWithoutExtension } ?: possibleTestBinaries.first() + } + } + } } diff --git a/vendor/vendor-ios/src/main/kotlin/com/malinskiy/marathon/ios/xctestrun/TestRootFactory.kt b/vendor/vendor-ios/src/main/kotlin/com/malinskiy/marathon/ios/xctestrun/TestRootFactory.kt index ff5be3905..b32175d6e 100644 --- a/vendor/vendor-ios/src/main/kotlin/com/malinskiy/marathon/ios/xctestrun/TestRootFactory.kt +++ b/vendor/vendor-ios/src/main/kotlin/com/malinskiy/marathon/ios/xctestrun/TestRootFactory.kt @@ -2,16 +2,14 @@ package com.malinskiy.marathon.ios.xctestrun import com.malinskiy.marathon.config.exceptions.ConfigurationException import com.malinskiy.marathon.config.vendor.VendorConfiguration -import com.malinskiy.marathon.config.vendor.ios.AppleTestBundleConfiguration import com.malinskiy.marathon.config.vendor.ios.TestType import com.malinskiy.marathon.exceptions.DeviceSetupException import com.malinskiy.marathon.exceptions.TransferException import com.malinskiy.marathon.ios.AppleSimulatorDevice import com.malinskiy.marathon.ios.RemoteFileManager +import com.malinskiy.marathon.ios.model.AppleTestBundle import com.malinskiy.marathon.ios.model.Arch import com.malinskiy.marathon.ios.model.Sdk -import com.malinskiy.marathon.ios.plist.PropertyList -import com.malinskiy.marathon.ios.plist.bundle.BundleInfo import com.malinskiy.marathon.ios.xctestrun.v2.Metadata import com.malinskiy.marathon.ios.xctestrun.v2.TestConfiguration import com.malinskiy.marathon.ios.xctestrun.v2.TestTarget @@ -20,15 +18,18 @@ import java.io.File import java.nio.file.Path import kotlin.io.path.createTempFile -class TestRootFactory(private val device: AppleSimulatorDevice, private val vendorConfiguration: VendorConfiguration.IOSConfiguration) { - suspend fun generate(testType: TestType, bundleConfiguration: AppleTestBundleConfiguration, useXctestParser: Boolean) { +class TestRootFactory( + private val device: AppleSimulatorDevice, + private val vendorConfiguration: VendorConfiguration.IOSConfiguration +) { + suspend fun generate(testType: TestType, bundle: AppleTestBundle, useXctestParser: Boolean) { val remoteFileManager = device.remoteFileManager val testRoot = remoteFileManager.remoteTestRoot() remoteFileManager.createRemoteDirectory(testRoot) val xctestrun = when (testType) { - TestType.XCUITEST -> generateXCUITest(testRoot, remoteFileManager, bundleConfiguration, useXctestParser) - TestType.XCTEST -> generateXCTest(testRoot, remoteFileManager, bundleConfiguration, useXctestParser) + TestType.XCUITEST -> generateXCUITest(testRoot, remoteFileManager, bundle, useXctestParser) + TestType.XCTEST -> generateXCTest(testRoot, remoteFileManager, bundle, useXctestParser) TestType.LOGIC_TEST -> TODO() } @@ -47,19 +48,15 @@ class TestRootFactory(private val device: AppleSimulatorDevice, private val vend private suspend fun generateXCUITest( testRoot: String, remoteFileManager: RemoteFileManager, - bundleConfiguration: AppleTestBundleConfiguration, + bundle: AppleTestBundle, useLibParseTests: Boolean ): Xctestrun { val sdkPlatformPath = device.binaryEnvironment.xcrun.getSdkPlatformPath(device.sdk) val platformLibraryPath = remoteFileManager.joinPath(sdkPlatformPath, "Developer", "Library") - val xctestBundleInfo: BundleInfo = PropertyList.from(File(bundleConfiguration.xctest, "Info.plist")) - val testBundleId = (xctestBundleInfo.naming.bundleName ?: bundleConfiguration.xctest.nameWithoutExtension).replace('-', '_') - val testRunnerApp = generateTestRunnerApp(testRoot, testBundleId, platformLibraryPath, bundleConfiguration) - val testApp = bundleConfiguration.app ?: throw ConfigurationException("no application specified for XCUITest") - val appBundleInfo: BundleInfo = PropertyList.from(File(testApp, "Info.plist")) - val appId = - appBundleInfo.identification.bundleIdentifier ?: throw ConfigurationException("No bundle identifier specified in $testApp") + val testRunnerApp = generateTestRunnerApp(testRoot, platformLibraryPath, bundle) + val testApp = bundle.application ?: throw ConfigurationException("no application specified for XCUITest") + val remoteTestApp = device.remoteFileManager.remoteApplication() if (!device.pushFolder(testApp, remoteTestApp)) { throw DeviceSetupException("failed to push app under test to remote device") @@ -91,7 +88,7 @@ class TestRootFactory(private val device: AppleSimulatorDevice, private val vend } else { emptyList() } - + /** * If the app contains internal frameworks we need to add them to xctestrun */ @@ -119,9 +116,9 @@ class TestRootFactory(private val device: AppleSimulatorDevice, private val vend "marathon", arrayOf( TestTarget.withArtifactReinstall( - name = testBundleId, + name = bundle.testBundleId, testingEnvironmentVariables = testEnv, - productModuleName = testBundleId, + productModuleName = bundle.testBundleId, systemAttachmentLifetime = vendorConfiguration.xcresult.attachments.systemAttachmentLifetime.value, userAttachmentLifetime = vendorConfiguration.xcresult.attachments.userAttachmentLifetime.value, dependentProductPaths = arrayOf(testRunnerApp, remoteXctest, remoteTestApp), @@ -139,16 +136,10 @@ class TestRootFactory(private val device: AppleSimulatorDevice, private val vend private suspend fun generateXCTest( testRoot: String, remoteFileManager: RemoteFileManager, - bundleConfiguration: AppleTestBundleConfiguration, + bundle: AppleTestBundle, useLibParseTests: Boolean ): Xctestrun { - val testApp = bundleConfiguration.app ?: throw ConfigurationException("no application specified for XCTest") - val xctestBundleInfo: BundleInfo = PropertyList.from(File(bundleConfiguration.xctest, "Info.plist")) - val testBundleId = (xctestBundleInfo.naming.bundleName ?: bundleConfiguration.xctest.nameWithoutExtension).replace('-', '_') - - val appBundleInfo: BundleInfo = PropertyList.from(File(testApp, "Info.plist")) - val appId = - appBundleInfo.identification.bundleIdentifier ?: throw ConfigurationException("No bundle identifier specified in $testApp") + val testApp = bundle.application ?: throw ConfigurationException("no application specified for XCTest") val remoteTestApp = device.remoteFileManager.remoteApplication() if (!device.pushFolder(testApp, remoteTestApp)) { @@ -159,12 +150,12 @@ class TestRootFactory(private val device: AppleSimulatorDevice, private val vend * A common scenario is to place xctest for unit tests inside the app's PlugIns. * This is what Xcode does out of the box */ - val remoteXctest = joinPath(remoteTestApp, "PlugIns", bundleConfiguration.xctest.name) + val remoteXctest = joinPath(remoteTestApp, "PlugIns", bundle.testApplication.name) remoteFileManager.createRemoteDirectory(joinPath(remoteTestApp, "PlugIns")) - if (bundleConfiguration.xctest == Path.of(testApp.path, "PlugIns")) { + if (bundle.testApplication == Path.of(testApp.path, "PlugIns")) { //We already pushed it above } else { - if (!device.pushFolder(bundleConfiguration.xctest, remoteXctest)) { + if (!device.pushFolder(bundle.testApplication, remoteXctest)) { throw DeviceSetupException("failed to push xctest to remote device") } } @@ -194,7 +185,7 @@ class TestRootFactory(private val device: AppleSimulatorDevice, private val vend if (File(testApp, "Frameworks").exists()) { dyldFrameworks.add("__TESTROOT__/${remoteFileManager.appUnderTestFileName()}/Frameworks") } - + val dyldLibraries = listOf("__TESTROOT__", usrLib, *userLibraryPath.toTypedArray()) val bundleInject = if (device.sdk == Sdk.IPHONEOS) { "__TESTHOST__/Frameworks/libXCTestBundleInject.dylib" @@ -223,9 +214,9 @@ class TestRootFactory(private val device: AppleSimulatorDevice, private val vend "marathon", arrayOf( TestTarget.withArtifactReinstall( - name = testBundleId, + name = bundle.testBundleId, testingEnvironmentVariables = testEnv, - productModuleName = testBundleId, + productModuleName = bundle.testBundleId, systemAttachmentLifetime = vendorConfiguration.xcresult.attachments.systemAttachmentLifetime.value, userAttachmentLifetime = vendorConfiguration.xcresult.attachments.userAttachmentLifetime.value, testBundlePath = remoteXctest, @@ -240,13 +231,12 @@ class TestRootFactory(private val device: AppleSimulatorDevice, private val vend private suspend fun generateTestRunnerApp( testRoot: String, - testBundleId: String, platformLibraryPath: String, - bundleConfiguration: AppleTestBundleConfiguration + bundle: AppleTestBundle, ): String { - val testBinary = joinPath(device.remoteFileManager.remoteXctestFile(), bundleConfiguration.xctest.nameWithoutExtension) + val testBinary = joinPath(device.remoteFileManager.remoteXctestFile(), bundle.testApplication.nameWithoutExtension) val baseApp = joinPath(platformLibraryPath, "Xcode", "Agents", "XCTRunner.app") - val runnerBinaryName = "$testBundleId-Runner" + val runnerBinaryName = "${bundle.testBundleId}-Runner" val testRunnerApp = joinPath(testRoot, "$runnerBinaryName.app") device.remoteFileManager.copy(baseApp, testRunnerApp)