Skip to content

Commit

Permalink
fix(apple): polish up the install
Browse files Browse the repository at this point in the history
  • Loading branch information
Malinskiy committed Feb 17, 2024
1 parent 07a5883 commit 02ece2b
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ open class AppleApplicationInstaller<in T: AppleDevice>(
val xctest = bundleConfiguration?.xctest ?: throw IllegalArgumentException("No test bundle provided")
val app = bundleConfiguration.app
val bundle = AppleTestBundle(app, xctest, device.sdk)
val relativeTestBinaryPath = bundle.relativeTestBinaryPath
val relativeTestBinaryPath = bundle.relativeBinaryPath

logger.debug { "Moving xctest to ${device.serialNumber}" }
val remoteXctest = device.remoteFileManager.remoteXctestFile()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class NmTestParser(
bundle: AppleTestBundle,
): List<Test> {
val testBinary = bundle.testBinary
val relativeTestBinaryPath = bundle.relativeTestBinaryPath
val relativeTestBinaryPath = bundle.relativeBinaryPath
val xctest = bundle.testApplication

logger.debug { "Found test binary $testBinary for xctest $xctest" }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.malinskiy.marathon.apple.bin

import com.google.gson.Gson
import com.malinskiy.marathon.apple.bin.chmod.Chmod
import com.malinskiy.marathon.apple.bin.codesign.Codesign
import com.malinskiy.marathon.apple.bin.getconf.Getconf
import com.malinskiy.marathon.apple.bin.ioreg.Ioreg
Expand Down Expand Up @@ -32,5 +33,6 @@ class AppleBinaryEnvironment(
val ioreg: Ioreg = Ioreg(commandExecutor, timeoutConfiguration)
val systemProfiler: SystemProfiler = SystemProfiler(commandExecutor, timeoutConfiguration)
val swvers: SwVers = SwVers(commandExecutor, timeoutConfiguration)
val chmod: Chmod = Chmod(commandExecutor, timeoutConfiguration)
val xcrun: Xcrun = Xcrun(commandExecutor, configuration, timeoutConfiguration, gson)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.malinskiy.marathon.apple.bin.chmod

import com.malinskiy.marathon.apple.cmd.CommandExecutor
import com.malinskiy.marathon.apple.cmd.CommandResult
import com.malinskiy.marathon.apple.extensions.bashEscape
import com.malinskiy.marathon.apple.model.Arch
import com.malinskiy.marathon.config.vendor.apple.TimeoutConfiguration
import com.malinskiy.marathon.exceptions.DeviceSetupException
import java.time.Duration

class Chmod(
private val commandExecutor: CommandExecutor,
private val timeoutConfiguration: TimeoutConfiguration,
) {
suspend fun makeExecutable(path: String): String {
return commandExecutor.criticalExecute(timeoutConfiguration.shell, "chmod", "+x", path.bashEscape()).successfulOrNull()?.combinedStdout?.trim()
?: throw DeviceSetupException("failed to detect operating system version")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,14 @@ class AppleTestBundle(
}
PropertyList.from(file)
}
val testBundleId = (testBundleInfo.naming.bundleName ?: testApplication.nameWithoutExtension).replace('-', '_')
val testBundleId = (testBundleInfo.naming.bundleName ?: testApplication.nameWithoutExtension).replace("[- ]".toRegex(), "_")

val testBinary: File by lazy {
val possibleTestBinaries = when (sdk) {
Sdk.IPHONEOS, Sdk.IPHONESIMULATOR -> testApplication.listFiles()?.filter { it.isFile && it.extension == "" }
?: throw ConfigurationException("missing test binaries in xctest folder at $testApplication")

Sdk.MACOS -> Paths.get(testApplication.absolutePath, *relativeTestBinaryPath).toFile().listFiles()
Sdk.MACOS -> Paths.get(testApplication.absolutePath, *relativeBinaryPath).toFile().listFiles()
?.filter { it.isFile && it.extension == "" }
?: throw ConfigurationException("missing test binaries in xctest folder at $testApplication")
}
Expand All @@ -60,10 +60,29 @@ class AppleTestBundle(
}
}

val applicationBinary: File? by lazy {
application?.let { application ->
when (sdk) {
Sdk.IPHONEOS, Sdk.IPHONESIMULATOR -> application.listFiles()?.filter { it.isFile && it.extension == "" }
Sdk.MACOS -> Paths.get(application.absolutePath, *relativeBinaryPath).toFile().listFiles()
?.filter { it.isFile && it.extension == "" }
}?.let { possibleBinaries ->
when (possibleBinaries.size) {
0 -> null
1 -> possibleBinaries[0]
else -> {
logger.warn { "Multiple application binaries present in app folder" }
possibleBinaries[0]
}
}
}
}
}

/**
* Path of the test binary relative to the xctest folder
* Path of the app and test binaries relative to the bundle's (app/xctest) folder
*/
val relativeTestBinaryPath: Array<String> by lazy {
val relativeBinaryPath: Array<String> by lazy {
when (sdk) {
Sdk.IPHONEOS, Sdk.IPHONESIMULATOR -> emptyArray()
Sdk.MACOS -> arrayOf("Contents", "MacOS")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ class TestRootFactory(
if (!device.pushFolder(testApp, remoteTestApp)) {
throw DeviceSetupException("failed to push app under test to remote device")
}
ensureApplicationBinaryIsExecutable(remoteFileManager, bundle)
val runnerPlugins = when (device.sdk) {
Sdk.IPHONEOS, Sdk.IPHONESIMULATOR -> remoteFileManager.joinPath(testRunnerApp, "PlugIns")
Sdk.MACOS -> remoteFileManager.joinPath(testRunnerApp, "Contents", "PlugIns")
Expand Down Expand Up @@ -150,6 +151,7 @@ class TestRootFactory(
if (!device.pushFolder(testApp, remoteTestApp)) {
throw DeviceSetupException("failed to push app under test to remote device")
}
ensureApplicationBinaryIsExecutable(remoteFileManager, bundle)

/**
* A common scenario is to place xctest for unit tests inside the app's PlugIns.
Expand All @@ -163,6 +165,7 @@ class TestRootFactory(
if (!device.pushFolder(bundle.testApplication, remoteXctest)) {
throw DeviceSetupException("failed to push xctest to remote device")
}
ensureApplicationBinaryIsExecutable(remoteFileManager, bundle)
}

if (device.sdk == Sdk.IPHONEOS) {
Expand Down Expand Up @@ -234,23 +237,37 @@ class TestRootFactory(
)
}

private suspend fun ensureApplicationBinaryIsExecutable(
remoteFileManager: RemoteFileManager,
bundle: AppleTestBundle
) {
bundle.applicationBinary?.let {
val remoteApplicationBinary = joinPath(
remoteFileManager.remoteApplication(),
*bundle.relativeBinaryPath,
it.name
)
device.binaryEnvironment.chmod.makeExecutable(remoteApplicationBinary)
}
}

private suspend fun generateTestRunnerApp(
testRoot: String,
platformLibraryPath: String,
bundle: AppleTestBundle,
): String {
val remoteTestBinary = joinPath(
device.remoteFileManager.remoteXctestFile(),
*bundle.relativeTestBinaryPath,
*bundle.relativeBinaryPath,
bundle.testBinary.nameWithoutExtension
)
val baseApp = joinPath(platformLibraryPath, "Xcode", "Agents", "XCTRunner.app")
val runnerBinaryName = "${bundle.testBundleId}-Runner"
val testRunnerApp = joinPath(testRoot, "$runnerBinaryName.app")
device.remoteFileManager.copy(baseApp, testRunnerApp)

val baseTestRunnerBinary = joinPath(testRunnerApp, *bundle.relativeTestBinaryPath, "XCTRunner")
val testRunnerBinary = joinPath(testRunnerApp, *bundle.relativeTestBinaryPath, runnerBinaryName)
val baseTestRunnerBinary = joinPath(testRunnerApp, *bundle.relativeBinaryPath, "XCTRunner")
val testRunnerBinary = joinPath(testRunnerApp, *bundle.relativeBinaryPath, runnerBinaryName)
device.remoteFileManager.copy(baseTestRunnerBinary, testRunnerBinary)

matchArchitectures(remoteTestBinary, testRunnerBinary)
Expand Down

0 comments on commit 02ece2b

Please sign in to comment.