1*dd0948b3SAndroid Build Coastguard Worker# Flicker Test Library 2*dd0948b3SAndroid Build Coastguard Worker 3*dd0948b3SAndroid Build Coastguard Worker## Motivation 4*dd0948b3SAndroid Build Coastguard WorkerDetect *flicker* — any discontinuous, or unpredictable behavior seen during UI transitions that is not due to performance. 5*dd0948b3SAndroid Build Coastguard WorkerThis is often the result of a logic error in the code and difficult to identify because the issue is transient and at times difficult to reproduce. 6*dd0948b3SAndroid Build Coastguard WorkerThis library helps create integration tests between `SurfaceFlinger`, `WindowManager` and `SystemUI` to identify flicker. 7*dd0948b3SAndroid Build Coastguard Worker 8*dd0948b3SAndroid Build Coastguard WorkerExamples of flickers are: 9*dd0948b3SAndroid Build Coastguard Worker* Elements drawn in the wrong location (e.g., not rotating correctly) 10*dd0948b3SAndroid Build Coastguard Worker* Empty areas on the screen (e.g., not all areas of the screen covered during rotation) 11*dd0948b3SAndroid Build Coastguard Worker* System elements not appearing (e.g., nav and status bar or split screen divider) 12*dd0948b3SAndroid Build Coastguard Worker* Elements (dis)appearing at the wrong time (e.g., an element becomes invisible before being completely occluded) 13*dd0948b3SAndroid Build Coastguard Worker 14*dd0948b3SAndroid Build Coastguard Worker## Usage and Capabilities 15*dd0948b3SAndroid Build Coastguard Worker 16*dd0948b3SAndroid Build Coastguard WorkerThe library builds and runs UI transitions, captures Winscope [traces](https://source.android.com/devices/graphics/tracing-win-transitions) and exposes common assertions that can be tested against each trace. 17*dd0948b3SAndroid Build Coastguard Worker 18*dd0948b3SAndroid Build Coastguard Worker## Building a transition 19*dd0948b3SAndroid Build Coastguard Worker 20*dd0948b3SAndroid Build Coastguard WorkerStart by defining common or error prone transitions using `TransitionRunner`. 21*dd0948b3SAndroid Build Coastguard Worker```java 22*dd0948b3SAndroid Build Coastguard Worker// Example: Build a transition that cold launches an app from launcher 23*dd0948b3SAndroid Build Coastguard WorkerTransitionRunner transition = new TransitionBuilder() 24*dd0948b3SAndroid Build Coastguard Worker // Specify a tag to identify the transition (optional) 25*dd0948b3SAndroid Build Coastguard Worker .withTag("OpenAppCold_" + testApp.getLauncherName()) 26*dd0948b3SAndroid Build Coastguard Worker 27*dd0948b3SAndroid Build Coastguard Worker // Specify preconditions to setup the device 28*dd0948b3SAndroid Build Coastguard Worker // Wake up device and go to home screen 29*dd0948b3SAndroid Build Coastguard Worker .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen) 30*dd0948b3SAndroid Build Coastguard Worker 31*dd0948b3SAndroid Build Coastguard Worker // Setup transition under test 32*dd0948b3SAndroid Build Coastguard Worker // Press the home button and close the app to test a cold start 33*dd0948b3SAndroid Build Coastguard Worker .runBefore(device::pressHome) 34*dd0948b3SAndroid Build Coastguard Worker .runBefore(testApp::exit) 35*dd0948b3SAndroid Build Coastguard Worker 36*dd0948b3SAndroid Build Coastguard Worker // Run the transition under test 37*dd0948b3SAndroid Build Coastguard Worker // Open the app and wait for UI to be idle 38*dd0948b3SAndroid Build Coastguard Worker // This is the part of the transition that will be tested. 39*dd0948b3SAndroid Build Coastguard Worker .run(testApp::open) 40*dd0948b3SAndroid Build Coastguard Worker .run(device::waitForIdle) 41*dd0948b3SAndroid Build Coastguard Worker 42*dd0948b3SAndroid Build Coastguard Worker // Perform any tear downs 43*dd0948b3SAndroid Build Coastguard Worker // Close the app 44*dd0948b3SAndroid Build Coastguard Worker .runAfterAll(testApp::exit) 45*dd0948b3SAndroid Build Coastguard Worker 46*dd0948b3SAndroid Build Coastguard Worker // Number of times to repeat the transition to catch any flaky issues 47*dd0948b3SAndroid Build Coastguard Worker .repeat(5); 48*dd0948b3SAndroid Build Coastguard Worker``` 49*dd0948b3SAndroid Build Coastguard Worker 50*dd0948b3SAndroid Build Coastguard WorkerRun the transition to get a list of `TransitionResult` for each time the transition is repeated. 51*dd0948b3SAndroid Build Coastguard Worker```java 52*dd0948b3SAndroid Build Coastguard Worker List<TransitionResult> results = transition.run(); 53*dd0948b3SAndroid Build Coastguard Worker``` 54*dd0948b3SAndroid Build Coastguard Worker`TransitionResult` contains paths to test artifacts such as Winscope traces and screen recordings. 55*dd0948b3SAndroid Build Coastguard Worker 56*dd0948b3SAndroid Build Coastguard Worker### Checking Assertions 57*dd0948b3SAndroid Build Coastguard WorkerEach `TransitionResult` can be tested using an extension of the Google Truth library, `LayersTraceSubject` and `WmTraceSubject`. 58*dd0948b3SAndroid Build Coastguard WorkerThey try to balance test principles set out by Google Truth (not supporting nested assertions, keeping assertions simple) with providing support for common assertion use cases. 59*dd0948b3SAndroid Build Coastguard Worker 60*dd0948b3SAndroid Build Coastguard WorkerEach trace can be represented as a ordered collection of trace entries, with an associated timestamp. 61*dd0948b3SAndroid Build Coastguard WorkerEach trace entry has common assertion checks. 62*dd0948b3SAndroid Build Coastguard WorkerThe trace subjects expose methods to filter the range of entries and test for changing assertions. 63*dd0948b3SAndroid Build Coastguard Worker 64*dd0948b3SAndroid Build Coastguard Worker```java 65*dd0948b3SAndroid Build Coastguard Worker TransitionResult result = results.get(0); 66*dd0948b3SAndroid Build Coastguard Worker Rect displayBounds = getDisplayBounds(); 67*dd0948b3SAndroid Build Coastguard Worker 68*dd0948b3SAndroid Build Coastguard Worker // check all trace entries 69*dd0948b3SAndroid Build Coastguard Worker assertThat(result).coversRegion(displayBounds).forAllEntries(); 70*dd0948b3SAndroid Build Coastguard Worker 71*dd0948b3SAndroid Build Coastguard Worker // check a range of entries 72*dd0948b3SAndroid Build Coastguard Worker assertThat(result).coversRegion(displayBounds).forRange(startTime, endTime); 73*dd0948b3SAndroid Build Coastguard Worker 74*dd0948b3SAndroid Build Coastguard Worker // check first entry 75*dd0948b3SAndroid Build Coastguard Worker assertThat(result).coversRegion(displayBounds).inTheBeginning(); 76*dd0948b3SAndroid Build Coastguard Worker 77*dd0948b3SAndroid Build Coastguard Worker // check last entry 78*dd0948b3SAndroid Build Coastguard Worker assertThat(result).coversRegion(displayBounds).atTheEnd(); 79*dd0948b3SAndroid Build Coastguard Worker 80*dd0948b3SAndroid Build Coastguard Worker // check a change in assertions, e.g. wallpaper window is visible, 81*dd0948b3SAndroid Build Coastguard Worker // then wallpaper window becomes and stays invisible 82*dd0948b3SAndroid Build Coastguard Worker assertThat(result) 83*dd0948b3SAndroid Build Coastguard Worker .showsBelowAppWindow("wallpaper") 84*dd0948b3SAndroid Build Coastguard Worker .then() 85*dd0948b3SAndroid Build Coastguard Worker .hidesBelowAppWindow("wallpaper") 86*dd0948b3SAndroid Build Coastguard Worker .forAllEntries(); 87*dd0948b3SAndroid Build Coastguard Worker``` 88*dd0948b3SAndroid Build Coastguard Worker 89*dd0948b3SAndroid Build Coastguard WorkerAll assertions return `Result` which contains a `success` flag, `assertionName` string identifier, and `reason` string to provide actionable details to the user. 90*dd0948b3SAndroid Build Coastguard WorkerThe `reason` string is build along the way with all the details as to why the assertions failed and any hints which might help the user determine the root cause. 91*dd0948b3SAndroid Build Coastguard WorkerFailed assertion message will also contain a path to the trace that was tested. 92*dd0948b3SAndroid Build Coastguard WorkerExample of a failed test: 93*dd0948b3SAndroid Build Coastguard Worker 94*dd0948b3SAndroid Build Coastguard Worker``` 95*dd0948b3SAndroid Build Coastguard Worker java.lang.AssertionError: Not true that <com.android.server.wm.traces.common.LayersTrace@65da4cc> 96*dd0948b3SAndroid Build Coastguard Worker Layers Trace can be found in: /layers_trace_emptyregion.pb 97*dd0948b3SAndroid Build Coastguard Worker Timestamp: 2308008331271 98*dd0948b3SAndroid Build Coastguard Worker Assertion: coversRegion 99*dd0948b3SAndroid Build Coastguard Worker Reason: Region to test: Rect(0, 0 - 1440, 2880) 100*dd0948b3SAndroid Build Coastguard Worker first empty point: 0, 99 101*dd0948b3SAndroid Build Coastguard Worker visible regions: 102*dd0948b3SAndroid Build Coastguard Worker StatusBar#0Rect(0, 0 - 1440, 98) 103*dd0948b3SAndroid Build Coastguard Worker NavigationBar#0Rect(0, 2712 - 1440, 2880) 104*dd0948b3SAndroid Build Coastguard Worker ScreenDecorOverlay#0Rect(0, 0 - 1440, 91) 105*dd0948b3SAndroid Build Coastguard Worker ... 106*dd0948b3SAndroid Build Coastguard Worker at com.google.common.truth.FailureStrategy.fail(FailureStrategy.java:24) 107*dd0948b3SAndroid Build Coastguard Worker ... 108*dd0948b3SAndroid Build Coastguard Worker``` 109*dd0948b3SAndroid Build Coastguard Worker 110*dd0948b3SAndroid Build Coastguard Worker--- 111*dd0948b3SAndroid Build Coastguard Worker 112*dd0948b3SAndroid Build Coastguard Worker## Running Tests 113*dd0948b3SAndroid Build Coastguard Worker 114*dd0948b3SAndroid Build Coastguard WorkerThe tests can be run as any other Android JUnit tests. `frameworks/base/tests/FlickerTests` uses the library to test common UI transitions. Run `atest FlickerTests` to execute these tests. 115*dd0948b3SAndroid Build Coastguard Worker 116*dd0948b3SAndroid Build Coastguard Worker--- 117*dd0948b3SAndroid Build Coastguard Worker 118*dd0948b3SAndroid Build Coastguard Worker## Other Topics 119*dd0948b3SAndroid Build Coastguard Worker### Monitors 120*dd0948b3SAndroid Build Coastguard WorkerMonitors capture test artifacts for each transition run. They are started before each iteration of the test transition (after the `runBefore` calls) and stopped after the transition is completed. Each iteration will produce a new test artifact. The following monitors are available: 121*dd0948b3SAndroid Build Coastguard Worker 122*dd0948b3SAndroid Build Coastguard Worker#### LayersTraceMonitor 123*dd0948b3SAndroid Build Coastguard WorkerCaptures Layers trace. This monitor is started by default. Build a transition with `skipLayersTrace()` to disable this monitor. 124*dd0948b3SAndroid Build Coastguard Worker#### WindowManagerTraceMonitor 125*dd0948b3SAndroid Build Coastguard WorkerCaptures Window Manager trace. This monitor is started by default. Build a transition with `skipWindowManagerTrace()` to disable this monitor. 126*dd0948b3SAndroid Build Coastguard Worker#### ScreenRecorder 127*dd0948b3SAndroid Build Coastguard WorkerCaptures screen to a video file. This monitor is disabled by default. Build a transition with `recordEachRun()` to capture each transition or build with `recordAllRuns()` to capture every transition including setup and teardown. 128*dd0948b3SAndroid Build Coastguard Worker 129*dd0948b3SAndroid Build Coastguard Worker--- 130*dd0948b3SAndroid Build Coastguard Worker 131*dd0948b3SAndroid Build Coastguard Worker### Extending Assertions 132*dd0948b3SAndroid Build Coastguard Worker 133*dd0948b3SAndroid Build Coastguard WorkerTo add a new assertion, add a function to one of the trace entry classes, `LayersTrace.Entry` or `WindowManagerTrace.Entry`. 134*dd0948b3SAndroid Build Coastguard Worker 135*dd0948b3SAndroid Build Coastguard Worker```java 136*dd0948b3SAndroid Build Coastguard Worker // Example adds an assertion to the check if layer is hidden by parent. 137*dd0948b3SAndroid Build Coastguard Worker Result isHiddenByParent(String layerName) { 138*dd0948b3SAndroid Build Coastguard Worker // Result should contain a details if assertion fails for any reason 139*dd0948b3SAndroid Build Coastguard Worker // such as if layer is not found or layer is not hidden by parent 140*dd0948b3SAndroid Build Coastguard Worker // or layer has no parent. 141*dd0948b3SAndroid Build Coastguard Worker // ... 142*dd0948b3SAndroid Build Coastguard Worker } 143*dd0948b3SAndroid Build Coastguard Worker``` 144*dd0948b3SAndroid Build Coastguard WorkerThen add a function to the trace subject `LayersTraceSubject` or `WmTraceSubject` which will add the assertion for testing. When the assertion is evaluated, the trace will first be filtered then the assertion will be applied to the remaining entries. 145*dd0948b3SAndroid Build Coastguard Worker 146*dd0948b3SAndroid Build Coastguard Worker```java 147*dd0948b3SAndroid Build Coastguard Worker public LayersTraceSubject isHiddenByParent(String layerName) { 148*dd0948b3SAndroid Build Coastguard Worker mChecker.add(entry -> entry.isHiddenByParent(layerName), 149*dd0948b3SAndroid Build Coastguard Worker "isHiddenByParent(" + layerName + ")"); 150*dd0948b3SAndroid Build Coastguard Worker return this; 151*dd0948b3SAndroid Build Coastguard Worker } 152*dd0948b3SAndroid Build Coastguard Worker``` 153*dd0948b3SAndroid Build Coastguard Worker 154*dd0948b3SAndroid Build Coastguard WorkerTo use the new assertion: 155*dd0948b3SAndroid Build Coastguard Worker```java 156*dd0948b3SAndroid Build Coastguard Worker // Check if "Chrome" layer is hidden by parent in the first trace entry. 157*dd0948b3SAndroid Build Coastguard Worker assertThat(result).isHiddenByParent("Chrome").inTheBeginning(); 158*dd0948b3SAndroid Build Coastguard Worker``` 159