mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-02 19:17:10 +00:00
test(sidecar): pin that a slow read counts toward the stability streak
This commit is contained in:
1 parent
9f1dfe27ba
commit
b1bf39bac5
2 files changed
+56
-7
No files matched your search
@@ -88,8 +88,14 @@ internal const val TRANSITION_POLL_INTERVAL_MILLIS = 100L
|
|||||||
// NavHost cross-fade is a 700ms tween (Compose navigation's default enter and
|
// NavHost cross-fade is a 700ms tween (Compose navigation's default enter and
|
||||||
// exit), it starts when the action lands rather than when the snapshot begins,
|
// exit), it starts when the action lands rather than when the snapshot begins,
|
||||||
// and the streak above has to fit after it. Measured on the emulator, a 600ms
|
// and the streak above has to fit after it. Measured on the emulator, a 600ms
|
||||||
// cap left about a third of fades unfinished. A layout that holds two routes at
|
// cap left about a third of fades unfinished.
|
||||||
// rest costs the full cap once per step and no more.
|
//
|
||||||
|
// A layout that holds two routes at rest pays the full cap on every snapshot
|
||||||
|
// RPC it is read with, and the runner issues more than one: fetchSyncedState
|
||||||
|
// (internal/runner) re-fetches a tree it considers transitional up to 4 times.
|
||||||
|
// It stops early once two consecutive fetches come back byte-identical, so such
|
||||||
|
// a layout costs 2 caps on a still tree and up to 4 on one that jitters
|
||||||
|
// underneath. Per step, not once per step.
|
||||||
internal const val TRANSITION_POLL_CAP_MILLIS = 1500L
|
internal const val TRANSITION_POLL_CAP_MILLIS = 1500L
|
||||||
|
|
||||||
// awaitSettledTree reads the hierarchy and, while the tree it gets back holds
|
// awaitSettledTree reads the hierarchy and, while the tree it gets back holds
|
||||||
@@ -123,11 +129,14 @@ internal fun awaitSettledTree(read: () -> String): String {
|
|||||||
// state stable.
|
// state stable.
|
||||||
//
|
//
|
||||||
// The streak is measured from the start of the read that opened the current
|
// The streak is measured from the start of the read that opened the current
|
||||||
// run of identical snapshots, not from when that read returned: a hierarchy
|
// run of identical snapshots, not from when that read returned, so a slow read
|
||||||
// fetch is not instantaneous, and the UI changing mid-fetch would have changed
|
// is charged to the streak. That is a deliberate trade and not a free one: the
|
||||||
// the snapshot, so the fetch's own duration is evidence of stability. On
|
// quiet the poll actually OBSERVED spans the last read's start back to the
|
||||||
// Android a fetch costs more than the poll interval, so charging it to the
|
// first read's return, which is shorter than streakMillis by up to the two
|
||||||
// streak is the difference between two reads and four.
|
// reads' durations. On Android a hierarchy fetch costs more than the poll
|
||||||
|
// interval, so charging it is the difference between two reads and four, and a
|
||||||
|
// caller wanting the full streak observed has to widen streakMillis rather than
|
||||||
|
// assume it. StabilityPollTest.slowSnapshotReadsCountTowardTheStreak pins this.
|
||||||
internal fun pollUntilStable(
|
internal fun pollUntilStable(
|
||||||
timeoutMillis: Long,
|
timeoutMillis: Long,
|
||||||
streakMillis: Long = MIN_STABLE_STREAK_MILLIS,
|
streakMillis: Long = MIN_STABLE_STREAK_MILLIS,
|
||||||
|
|||||||
@@ -16,6 +16,46 @@ class StabilityPollTest {
|
|||||||
assertTrue(elapsed < 3000L, "should not run to cap when stable, elapsed=${elapsed}ms")
|
assertTrue(elapsed < 3000L, "should not run to cap when stable, elapsed=${elapsed}ms")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test fun slowSnapshotReadsCountTowardTheStreak() {
|
||||||
|
// The streak runs from the START of the read that opened the run of
|
||||||
|
// identical snapshots, so the read's own duration is charged to it.
|
||||||
|
// Every other test here uses an instant lambda and so passes under
|
||||||
|
// either semantics; this one is the difference. StubDriverBackend's
|
||||||
|
// waitForIdle polls a real `uiautomator dump`, which costs hundreds of
|
||||||
|
// milliseconds, so the slow read is the case it runs in.
|
||||||
|
val readMillis = 400L
|
||||||
|
val sampleStarts = mutableListOf<Long>()
|
||||||
|
val sampleEnds = mutableListOf<Long>()
|
||||||
|
val start = System.currentTimeMillis()
|
||||||
|
pollUntilStable(5000L) {
|
||||||
|
sampleStarts += System.currentTimeMillis() - start
|
||||||
|
Thread.sleep(readMillis)
|
||||||
|
sampleEnds += System.currentTimeMillis() - start
|
||||||
|
"stable"
|
||||||
|
}
|
||||||
|
val elapsed = System.currentTimeMillis() - start
|
||||||
|
|
||||||
|
// One read plus one interval plus one read already clears 750ms, so the
|
||||||
|
// poll settles for two samples where an instant read takes four.
|
||||||
|
assertEquals(
|
||||||
|
2,
|
||||||
|
sampleStarts.size,
|
||||||
|
"a ${readMillis}ms read should clear the streak in two samples, starts=$sampleStarts",
|
||||||
|
)
|
||||||
|
assertTrue(elapsed >= MIN_STABLE_STREAK_MILLIS, "elapsed=${elapsed}ms")
|
||||||
|
|
||||||
|
// What the poll actually watched: the last read began this long after
|
||||||
|
// the first one returned. It is the poll interval, not the streak, and
|
||||||
|
// a caller that needs MIN_STABLE_STREAK_MILLIS of observed quiet has to
|
||||||
|
// ask for a wider streak rather than assume this one delivers it.
|
||||||
|
val observedQuiet = sampleStarts.last() - sampleEnds.first()
|
||||||
|
assertTrue(
|
||||||
|
observedQuiet < MIN_STABLE_STREAK_MILLIS,
|
||||||
|
"the poll returned having observed ${observedQuiet}ms of quiet, not " +
|
||||||
|
"${MIN_STABLE_STREAK_MILLIS}ms; if that changed, the streak semantics changed",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Test fun streakResetsOnAnyChange() {
|
@Test fun streakResetsOnAnyChange() {
|
||||||
// A late transition that fires after the prior streak has already
|
// A late transition that fires after the prior streak has already
|
||||||
// begun must reset the clock: the post-transition stable window has
|
// begun must reset the clock: the post-transition stable window has
|
||||||
|
|||||||
Reference in new issue
Block a user