Make the search benchmark start from a known screen, so all four measure

The failure was not the app bar at all. Dumping the hierarchy at the point of failure showed a TextView whose text was 'Search this folder' and a desc='Clear search' beside it: the app was already in search mode, because the previous iteration left it there and CompilationMode.Ignore() reinstalls nothing between iterations. So the setup found a search field where it expected an action. openFixture now calls killProcess() before each iteration, and the test writes search-action-missing.xml into its external files directory when the action is missing, which is what turned this from a guess into a diagnosis.

With that, one run gives OK (4 tests): coldStart 531.6 ms median (475.6-552.0), opening a 1000-file folder 12 frames (12-17), scrolling it with four swipes 97 frames (92-100), and typing into search 39 frames (36-40). The run before it, same method, gave 519.5 ms / 14 / 94 / 38, so the numbers repeat. PERFORMANCE.md now carries both runs side by side and only lists what is genuinely still missing: larger folders, the photo grid, and any jank figure at all, since this phone reports frame counts and no frame durations.
This commit is contained in:
2026-10-17 15:58:03 +02:00
parent 7514676384
commit cbb36abfce
3 changed files with 52 additions and 28 deletions
@@ -8,9 +8,11 @@ import androidx.benchmark.macro.StartupMode
import androidx.benchmark.macro.StartupTimingMetric
import androidx.benchmark.macro.junit4.MacrobenchmarkRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until
import java.io.File
import org.junit.Assert.assertNotNull
import org.junit.Rule
import org.junit.Test
@@ -161,9 +163,21 @@ class BrowserBenchmark {
// The measured block clicks this, so it has to exist before the clock starts: right after a
// thousand rows render, the app bar is a frame behind the list. Ten seconds was not enough on
// this phone, so this one step gets twice that — it is setup, not measurement.
val searchAction = device.wait(Until.findObject(By.desc(SEARCH_ACTION)), SLOW_TIMEOUT_MS)
if (searchAction == null) {
// Leave the screen behind for whoever reads the failure: guessing what "the app bar is
// missing" looked like cost an afternoon. `adb pull` this file from the test app's
// external files directory.
val dump = File(
InstrumentationRegistry.getInstrumentation().context.getExternalFilesDir(null),
"search-action-missing.xml",
)
device.dumpWindowHierarchy(dump)
System.err.println("hierarchy dumped to ${dump.absolutePath}")
}
assertNotNull(
"the app bar's search action is missing; see browser_search_hint in strings.xml",
device.wait(Until.findObject(By.desc(SEARCH_ACTION)), SLOW_TIMEOUT_MS),
searchAction,
)
},
) {
@@ -182,6 +196,10 @@ class BrowserBenchmark {
* instruction that is missing if something is not where it should be.
*/
private fun MacrobenchmarkScope.openFixture(device: UiDevice) {
// Kill the app first. Each iteration must start from the roots screen, and the activity keeps its
// state otherwise: the search iteration left search mode open, so the next one found a search field
// where it expected an app bar — measured, by dumping the hierarchy at the point of failure.
killProcess()
pressHome()
startActivityAndWait()
grantStorageAccess(device)