Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 75921283 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Move FlickerTests to frameworks/base/tests 2/2"

parents e20cd739 8248b7c9
Loading
Loading
Loading
Loading
+35 −0
Original line number Diff line number Diff line
#
# Copyright (C) 2018 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

LOCAL_PACKAGE_NAME := FlickerTests
LOCAL_MODULE_TAGS := tests optional
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
LOCAL_COMPATIBILITY_SUITE := device-tests

LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_STATIC_JAVA_LIBRARIES := \
    flickertestapplib \
    flickerlib \
    truth-prebuilt \
    app-helpers-core

include $(BUILD_PACKAGE)
include $(call all-makefiles-under,$(LOCAL_PATH))
 No newline at end of file
+36 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2018 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.android.server.wm.flicker">

    <uses-sdk android:minSdkVersion="27" android:targetSdkVersion="27"/>
    <!-- Read and write traces from external storage -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <!-- Capture screen contents -->
    <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
    <!-- Run layers trace -->
    <uses-permission android:name="android.permission.HARDWARE_TEST"/>
    <application>
        <uses-library android:name="android.test.runner"/>
    </application>

    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                     android:targetPackage="com.android.server.wm.flicker"
                     android:label="WindowManager Flicker Tests">
    </instrumentation>
</manifest>
 No newline at end of file
+28 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
 * Copyright 2018 Google Inc. All Rights Reserved.
 -->
<configuration description="Runs WindowManager Flicker Tests">
    <option name="test-tag" value="FlickerTests" />
    <target_preparer class="com.google.android.tradefed.targetprep.GoogleDeviceSetup">
        <!-- keeps the screen on during tests -->
        <option name="screen-always-on" value="on" />
        <!-- prevents the phone from restarting -->
        <option name="force-skip-system-props" value="true" />
    </target_preparer>
    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
        <option name="cleanup-apks" value="true"/>
        <option name="test-file-name" value="FlickerTests.apk"/>
        <option name="test-file-name" value="FlickerTestApp.apk" />
    </target_preparer>
    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
        <option name="package" value="com.android.server.wm.flicker"/>
        <option name="shell-timeout" value="6600s" />
        <option name="test-timeout" value="6000s" />
        <option name="hidden-api-checks" value="false" />
    </test>
    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
        <option name="directory-keys" value="/sdcard/flicker" />
        <option name="collect-on-run-ended-only" value="true" />
    </metrics_collector>
</configuration>
+146 −0
Original line number Diff line number Diff line
# Flicker Test Library

## Motivation
Detect *flicker* &mdash; any discontinuous, or unpredictable behavior seen during UI transitions that is not due to performance. This 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. This library helps create integration tests between `SurfaceFlinger`, `WindowManager` and `SystemUI` to identify flicker.

## Adding a Test
The library builds and runs UI transitions, captures Winscope traces and exposes common assertions that can be tested against each trace.

### Building Transitions
Start by defining common or error prone transitions using `TransitionRunner`.
```java
// Example: Build a transition that cold launches an app from launcher
TransitionRunner transition = TransitionRunner.newBuilder()
                // Specify a tag to identify the transition (optional)
                .withTag("OpenAppCold_" + testApp.getLauncherName())

                // Specify preconditions to setup the device
                // Wake up device and go to home screen
                .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)

                // Setup transition under test
                // Press the home button and close the app to test a cold start
                .runBefore(device::pressHome)
                .runBefore(testApp::exit)

                // Run the transition under test
                // Open the app and wait for UI to be idle
                // This is the part of the transition that will be tested.
                .run(testApp::open)
                .run(device::waitForIdle)

                // Perform any tear downs
                // Close the app
                .runAfterAll(testApp::exit)

                // Number of times to repeat the transition to catch any flaky issues
                .repeat(5);
```


Run the transition to get a list of `TransitionResult` for each time the transition is repeated.
```java
    List<TransitionResult> results = transition.run();
```
`TransitionResult` contains paths to test artifacts such as Winscope traces and screen recordings.


### Checking Assertions
Each `TransitionResult` can be tested using an extension of the Google Truth library, `LayersTraceSubject` and `WmTraceSubject`. They 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.

Each trace can be represented as a ordered collection of trace entries, with an associated timestamp. Each trace entry has common assertion checks. The trace subjects expose methods to filter the range of entries and test for changing assertions.

```java
    TransitionResult result = results.get(0);
    Rect displayBounds = getDisplayBounds();

    // check all trace entries
    assertThat(result).coversRegion(displayBounds).forAllEntries();

    // check a range of entries
    assertThat(result).coversRegion(displayBounds).forRange(startTime, endTime);

    // check first entry
    assertThat(result).coversRegion(displayBounds).inTheBeginning();

    // check last entry
    assertThat(result).coversRegion(displayBounds).atTheEnd();

    // check a change in assertions, e.g. wallpaper window is visible,
    // then wallpaper window becomes and stays invisible
    assertThat(result)
                .showsBelowAppWindow("wallpaper")
                .then()
                .hidesBelowAppWindow("wallpaper")
                .forAllEntries();
```

All assertions return `Result` which contains a `success` flag, `assertionName` string identifier, and `reason` string to provide actionable details to the user. The `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. Failed assertion message will also contain a path to the trace that was tested. Example of a failed test:

```
    java.lang.AssertionError: Not true that <com.android.server.wm.flicker.LayersTrace@65da4cc>
    Layers Trace can be found in: /layers_trace_emptyregion.pb
    Timestamp: 2308008331271
    Assertion: coversRegion
    Reason:   Region to test: Rect(0, 0 - 1440, 2880)
    first empty point: 0, 99
    visible regions:
    StatusBar#0Rect(0, 0 - 1440, 98)
    NavigationBar#0Rect(0, 2712 - 1440, 2880)
    ScreenDecorOverlay#0Rect(0, 0 - 1440, 91)
    ...
        at com.google.common.truth.FailureStrategy.fail(FailureStrategy.java:24)
        ...
```

---

## Running Tests

The tests can be run as any other Android JUnit tests. `platform_testing/tests/flicker` uses the library to test common UI transitions. Run `atest FlickerTest` to execute these tests.

---

## Other Topics
### Monitors
Monitors 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:

#### LayersTraceMonitor
Captures Layers trace. This monitor is started by default. Build a transition with `skipLayersTrace()` to disable this monitor.
#### WindowManagerTraceMonitor
Captures Window Manager trace. This monitor is started by default. Build a transition with `skipWindowManagerTrace()` to disable this monitor.
#### WindowAnimationFrameStatsMonitor
Captures WindowAnimationFrameStats for the transition. This monitor is started by default and is used to eliminate *janky* runs. If an iteration has skipped frames, as determined by WindowAnimationFrameStats, the results for the iteration is skipped. If the list of results is empty after all iterations are completed, then the test should fail. Build a transition with `includeJankyRuns()` to disable this monitor.
#### ScreenRecorder
Captures 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.

---

### Extending Assertions

To add a new assertion, add a function to one of the trace entry classes, `LayersTrace.Entry` or `WindowManagerTrace.Entry`.

```java
    // Example adds an assertion to the check if layer is hidden by parent.
    Result isHiddenByParent(String layerName) {
        // Result should contain a details if assertion fails for any reason
        // such as if layer is not found or layer is not hidden by parent
        // or layer has no parent.
        // ...
    }
```
Then 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.

```java
    public LayersTraceSubject isHiddenByParent(String layerName) {
        mChecker.add(entry -> entry.isHiddenByParent(layerName),
                "isHiddenByParent(" + layerName + ")");
        return this;
    }
```

To use the new assertion:
```java
    // Check if "Chrome" layer is hidden by parent in the first trace entry.
    assertThat(result).isHiddenByParent("Chrome").inTheBeginning();
```
 No newline at end of file
+7 −0
Original line number Diff line number Diff line
{
  "postsubmit": [
    {
      "name": "FlickerTests"
    }
  ]
}
 No newline at end of file
Loading