mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-02 11:07:10 +00:00
refactor(sidecar): drop IosDriverBackend
This commit is contained in:
1 parent
406b7516b3
commit
7c0f9a0c95
1 file changed
-160
@@ -759,166 +759,6 @@ internal class WdaRecovery(
|
||||
}
|
||||
}
|
||||
|
||||
class IosDriverBackend(private val udid: String) : DriverBackend {
|
||||
private lateinit var driver: maestro.drivers.IOSDriver
|
||||
private lateinit var localDevice: ios.LocalIOSDevice
|
||||
private lateinit var installer: xcuitest.installer.LocalXCTestInstaller
|
||||
private val recovery by lazy {
|
||||
WdaRecovery(
|
||||
isAlive = { runCatching { installer.isChannelAlive() }.getOrElse { false } },
|
||||
restart = { driver.open(); warmup() },
|
||||
)
|
||||
}
|
||||
|
||||
init {
|
||||
val reaped = reapOrphanIosRunners(udid) { command ->
|
||||
runCatching { ProcessBuilder(command).start().waitFor() }.getOrDefault(1)
|
||||
}
|
||||
if (reaped) {
|
||||
println("terminated orphaned XCTest runner session for $udid")
|
||||
// Give the killed session a beat to tear down before installing ours.
|
||||
Thread.sleep(1000)
|
||||
}
|
||||
val wdaPort = maestro.utils.SocketUtils.nextFreePort(22000, 23000)
|
||||
val tempFileHandler = maestro.utils.TempFileHandler()
|
||||
val simctlDevice = device.SimctlIOSDevice(
|
||||
deviceId = udid,
|
||||
tempFileHandler = tempFileHandler,
|
||||
)
|
||||
val driverConfig = xcuitest.installer.LocalXCTestInstaller.IOSDriverConfig(
|
||||
prebuiltRunner = true,
|
||||
sourceDirectory = "driver-iPhoneSimulator",
|
||||
context = xcuitest.installer.Context.CLI,
|
||||
snapshotKeyHonorModalViews = null,
|
||||
)
|
||||
installer = xcuitest.installer.LocalXCTestInstaller(
|
||||
deviceId = udid,
|
||||
host = "127.0.0.1",
|
||||
deviceType = util.IOSDeviceType.SIMULATOR,
|
||||
defaultPort = wdaPort,
|
||||
iOSDriverConfig = driverConfig,
|
||||
deviceController = simctlDevice,
|
||||
tempFileHandler = tempFileHandler,
|
||||
)
|
||||
val xcTestClient = xcuitest.XCTestClient("127.0.0.1", wdaPort)
|
||||
val xcTestDriverClient = xcuitest.XCTestDriverClient(
|
||||
installer = installer,
|
||||
client = xcTestClient,
|
||||
)
|
||||
val xcRunnerUtils = util.XCRunnerCLIUtils(tempFileHandler)
|
||||
val xcTestDevice = ios.xctest.XCTestIOSDevice(
|
||||
deviceId = udid,
|
||||
client = xcTestDriverClient,
|
||||
getInstalledApps = { xcRunnerUtils.listApps(udid) },
|
||||
)
|
||||
val device = ios.LocalIOSDevice(
|
||||
deviceId = udid,
|
||||
xcTestDevice = xcTestDevice,
|
||||
deviceController = simctlDevice,
|
||||
insights = maestro.utils.NoopInsights,
|
||||
)
|
||||
localDevice = device
|
||||
driver = maestro.drivers.IOSDriver(device, maestro.utils.NoopInsights)
|
||||
driver.open()
|
||||
warmup()
|
||||
}
|
||||
|
||||
private fun warmup() {
|
||||
var warmupErr: Exception? = null
|
||||
repeat(3) { attempt ->
|
||||
try {
|
||||
driver.contentDescriptor(false)
|
||||
warmupErr = null
|
||||
return@repeat
|
||||
} catch (e: Exception) {
|
||||
warmupErr = e
|
||||
if (attempt < 2) Thread.sleep(500)
|
||||
}
|
||||
}
|
||||
warmupErr?.let { throw IllegalStateException("WDA warmup failed after 3 attempts: $it") }
|
||||
}
|
||||
|
||||
private fun <T> withReconnect(replay: Boolean = true, block: () -> T): T =
|
||||
recovery.run(replay, block)
|
||||
|
||||
override fun launch(bundleId: String, clearState: Boolean, env: Map<String, String>) = withReconnect {
|
||||
runCatching { driver.stopApp(bundleId) }
|
||||
if (clearState) driver.clearAppState(bundleId)
|
||||
driver.launchApp(bundleId, env)
|
||||
}
|
||||
|
||||
override fun terminate(bundleId: String) = withReconnect { driver.stopApp(bundleId) }
|
||||
|
||||
override fun tap(x: Int, y: Int) = withReconnect(replay = false) { driver.tap(maestro.Point(x, y)) }
|
||||
|
||||
// The second tap request is already queued at the XCTest runner while the
|
||||
// first executes, so the on-device gap collapses to the runner's
|
||||
// turnaround instead of a full transport round trip. Sequential requests
|
||||
// leave a gap wide enough for the app to navigate between the taps.
|
||||
override fun doubleTap(x: Int, y: Int): Unit = withReconnect(replay = false) {
|
||||
overlappedDoubleTap { driver.tap(maestro.Point(x, y)) }
|
||||
}
|
||||
|
||||
override fun longPress(x: Int, y: Int) = withReconnect(replay = false) { driver.longPress(maestro.Point(x, y)) }
|
||||
|
||||
override fun tapSelector(selector: String) = withReconnect(replay = false) {
|
||||
val root = driver.contentDescriptor(false)
|
||||
val bounds = findBoundsBySelector(root, selector) ?: return@withReconnect
|
||||
driver.tap(maestro.Point((bounds[0] + bounds[2]) / 2, (bounds[1] + bounds[3]) / 2))
|
||||
}
|
||||
|
||||
override fun inputText(text: String) = withReconnect(replay = false) { driver.inputText(text) }
|
||||
|
||||
override fun eraseText(characterCount: Int) = withReconnect(replay = false) { driver.eraseText(characterCount) }
|
||||
|
||||
override fun swipe(fromX: Int, fromY: Int, toX: Int, toY: Int, durationMillis: Long) = withReconnect(replay = false) {
|
||||
driver.swipe(maestro.Point(fromX, fromY), maestro.Point(toX, toY), maxOf(durationMillis, 250L))
|
||||
}
|
||||
|
||||
override fun pressKey(key: String) = withReconnect(replay = false) {
|
||||
StubDriverBackend.KEY_MAP[key]?.let { keyCode ->
|
||||
keyCodeToMaestro(keyCode)?.let { driver.pressKey(it) }
|
||||
}
|
||||
Unit
|
||||
}
|
||||
|
||||
override fun screenshot(): Triple<ByteArray, Int, Int> = withReconnect {
|
||||
val buf = okio.Buffer()
|
||||
driver.takeScreenshot(buf, false)
|
||||
val bytes = buf.readByteArray()
|
||||
Triple(bytes, pngWidth(bytes), pngHeight(bytes))
|
||||
}
|
||||
|
||||
override fun hierarchy(): String = withReconnect {
|
||||
com.fasterxml.jackson.module.kotlin.jacksonObjectMapper()
|
||||
.writeValueAsString(iosAxElementToTreeNode(localDevice.viewHierarchy(false).axElement))
|
||||
}
|
||||
|
||||
override fun recentLogs(sinceUnixMillis: Long, minLevel: String): List<LogLine> = emptyList()
|
||||
|
||||
override fun waitForIdle(durationMillis: Long) = withReconnect {
|
||||
driver.waitForAppToSettle(null, null, durationMillis.toInt())
|
||||
pollUntilStable(STABILITY_POLL_CAP_MILLIS) {
|
||||
stabilitySnapshot(
|
||||
com.fasterxml.jackson.module.kotlin.jacksonObjectMapper()
|
||||
.writeValueAsString(driver.contentDescriptor(false)),
|
||||
)
|
||||
}
|
||||
Unit
|
||||
}
|
||||
|
||||
override fun healthy() = runCatching { driver.contentDescriptor(false); true }.getOrElse { false }
|
||||
|
||||
override fun metrics(bundleId: String) = MetricsSample(0.0, 0L, 0L)
|
||||
|
||||
// close stops the XCTest runner session (kills the xcodebuild process and
|
||||
// uninstalls the runner app). Skipping this leaves an orphaned session
|
||||
// that xcodebuild later restarts, killing the next run's session.
|
||||
override fun close() {
|
||||
runCatching { driver.close() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun keyCodeToMaestro(adbKeyCode: String): maestro.KeyCode? {
|
||||
return when (adbKeyCode) {
|
||||
"KEYCODE_BACK" -> maestro.KeyCode.BACK
|
||||
|
||||
Reference in new issue
Block a user