mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-02 19:17:10 +00:00
fix(sidecar): cap stability poll independently of settle budget
The previous shape halved durationMillis between waitForAppToSettle and the structural poll, then hammered hierarchy() at 80ms intervals - on Maestro this stacked enough RPCs that hierarchy fetches began timing out under load and the run stalled. Pass the full budget to waitForAppToSettle and cap the follow-up structural poll at 600ms with a 120ms interval, so the device sees at most a handful of extra hierarchy reads per step.
This commit is contained in:
1 parent
d0cf5a98c7
commit
a10791e00f
1 file changed
+17
-17
@@ -16,7 +16,8 @@ interface DriverBackend {
|
||||
fun metrics(bundleId: String): MetricsSample
|
||||
}
|
||||
|
||||
internal const val STABILITY_POLL_INTERVAL_MILLIS = 80L
|
||||
internal const val STABILITY_POLL_INTERVAL_MILLIS = 120L
|
||||
internal const val STABILITY_POLL_CAP_MILLIS = 600L
|
||||
|
||||
// pollUntilStable returns as soon as two consecutive snapshot() results are
|
||||
// structurally identical, capped at timeoutMillis. snapshot must omit
|
||||
@@ -424,17 +425,17 @@ class StubDriverBackend(private val platform: String) : DriverBackend {
|
||||
|
||||
override fun waitForIdle(durationMillis: Long) {
|
||||
// Two-stage settle: an mAnimating poll catches View-system animations,
|
||||
// then a structural-hash poll catches Compose cross-fades where two
|
||||
// composables are simultaneously alive but mAnimating is already false.
|
||||
// then a short structural-hash poll catches Compose cross-fades where
|
||||
// two composables are simultaneously alive but mAnimating is already
|
||||
// false. The structural poll is hard-capped so we don't hammer the
|
||||
// device with repeat hierarchy fetches when the UI never stabilizes.
|
||||
if (durationMillis <= 0) return
|
||||
val animationBudget = durationMillis / 2
|
||||
val animationDeadline = System.currentTimeMillis() + animationBudget
|
||||
val animationDeadline = System.currentTimeMillis() + durationMillis
|
||||
while (System.currentTimeMillis() < animationDeadline) {
|
||||
if (isDeviceIdle()) break
|
||||
Thread.sleep(IDLE_POLL_INTERVAL_MILLIS)
|
||||
}
|
||||
val structuralBudget = durationMillis - animationBudget
|
||||
pollUntilStable(structuralBudget) { structuralHash(hierarchy()) }
|
||||
pollUntilStable(STABILITY_POLL_CAP_MILLIS) { structuralHash(hierarchy()) }
|
||||
}
|
||||
|
||||
private fun isDeviceIdle(): Boolean {
|
||||
@@ -504,12 +505,13 @@ class MaestroDriverBackend(private val serial: String?) : DriverBackend {
|
||||
override fun waitForIdle(durationMillis: Long) {
|
||||
// waitForAppToSettle returns early on Compose cross-fade transitions
|
||||
// where both source and destination composables are semantically
|
||||
// alive; a follow-up structural-hash poll lands on a single stable
|
||||
// frame before the runner reads hierarchy + screenshot concurrently.
|
||||
val maestroBudget = durationMillis / 2
|
||||
val stabilityBudget = durationMillis - maestroBudget
|
||||
driver.waitForAppToSettle(null, null, maestroBudget.toInt())
|
||||
pollUntilStable(stabilityBudget) {
|
||||
// alive; a follow-up short structural-hash poll lands on a single
|
||||
// stable frame before the runner reads hierarchy + screenshot
|
||||
// concurrently. The structural poll is hard-capped independently of
|
||||
// durationMillis so we don't pile on hierarchy fetches when settle
|
||||
// never converges.
|
||||
driver.waitForAppToSettle(null, null, durationMillis.toInt())
|
||||
pollUntilStable(STABILITY_POLL_CAP_MILLIS) {
|
||||
structuralHash(
|
||||
com.fasterxml.jackson.module.kotlin.jacksonObjectMapper()
|
||||
.writeValueAsString(driver.contentDescriptor(false)),
|
||||
@@ -701,10 +703,8 @@ class IosDriverBackend(private val udid: String) : DriverBackend {
|
||||
override fun recentLogs(sinceUnixMillis: Long, minLevel: String): List<LogLine> = emptyList()
|
||||
|
||||
override fun waitForIdle(durationMillis: Long) = withReconnect {
|
||||
val maestroBudget = durationMillis / 2
|
||||
val stabilityBudget = durationMillis - maestroBudget
|
||||
driver.waitForAppToSettle(null, null, maestroBudget.toInt())
|
||||
pollUntilStable(stabilityBudget) {
|
||||
driver.waitForAppToSettle(null, null, durationMillis.toInt())
|
||||
pollUntilStable(STABILITY_POLL_CAP_MILLIS) {
|
||||
structuralHash(
|
||||
com.fasterxml.jackson.module.kotlin.jacksonObjectMapper()
|
||||
.writeValueAsString(driver.contentDescriptor(false)),
|
||||
|
||||
Reference in new issue
Block a user