| # Scenario App: Android Tests and Test Runner | |
| End-to-end tests and test infrastructure for the Flutter engine on Android. | |
| > [!IMPORTANT] | |
| > There are several **known issues** with this test suite: | |
| > | |
| > - [#144407](https://github.com/flutter/flutter/issues/144407): "Newer Android" | |
| > tests are missing expected cropping and rotation. | |
| > - [#144365](https://github.com/flutter/flutter/issues/144365): Tests that use | |
| > `ExternalTextureFlutterActivity` sometimes do not render. | |
| > - [#144232](https://github.com/flutter/flutter/issues/144232): Impeller Vulkan | |
| > sometimes hangs on emulators. | |
| > - [#144352](https://github.com/flutter/flutter/issues/144352): Skia Gold | |
| > sometimes is missing expected diffs. | |
| --- | |
| Top topics covered in this document include (but are not limited to): | |
| - [Running the Tests](#running-the-tests) | |
| - [Contributing](#contributing) | |
| - [Project History](#project-history) | |
| - [Troubleshooting](#troubleshooting) | |
| - [Getting Help](#getting-help) | |
| ## Introduction | |
| This package simulates a Flutter app that uses the engine (`dart:ui`) only, in | |
| conjunction with Android-specific embedding code that simulates the use of the | |
| engine in a real app (such as plugins and platform views). | |
| A custom test runner, [`run_android_tests.dart`](bin/run_android_tests.dart), is | |
| used to run the tests on a connected device or emulator, and report the results, | |
| including screenshots (golden files) and logs from `adb logcat`. | |
| In the following architecture diagram: | |
| > <a | |
| > href="https://github.com/flutter/flutter/wiki/The-Engine-architecture" | |
| > title="The Engine architecture"> <img | |
| > width="300" | |
| > alt="Anatomy of a Flutter app" | |
| > src="https://raw.githubusercontent.com/flutter/engine/main/docs/app_anatomy.svg" | |
| > /> </a> | |
| - The Dart app is represented by [`lib/main.dart`](../lib/main.dart). | |
| - There is no framework code. | |
| - `dart:ui` and the engine are the same as in a real app. | |
| - Android-specific application code is in this directory | |
| ([`android/`](./)). | |
| - The runner is a _custom_ test runner, | |
| [`run_android_tests.dart`](bin/run_android_tests.dart). | |
| ### Scope of Testing | |
| The tests in this package are end-to-end tests that _specifically_ exercise the | |
| engine and Android-specific embedding code. They are not [unit tests][1] for the | |
| engine or the framework, and are not designed to test the behavior of the | |
| framework or specific plugins, but rather to simulate the use of a framework | |
| or plugins downstream of the engine. | |
| In other words, we test "does the engine work on Android?" without a dependency | |
| on either the Flutter framework, Flutter tooling, or any specific plugins. | |
| [1]: ../../../shell/platform/android | |
| ### Golden Comparisons | |
| Many of the Android-specific interactions with the engine are visual, such as | |
| [external textures](https://api.flutter.dev/flutter/widgets/Texture-class.html) | |
| or [platform views](https://docs.flutter.dev/platform-integration/android/platform-views), | |
| and as such, the tests in this package use golden screenshot file comparisons to | |
| verify the correctness of the engine's output. | |
| For example, in [`ExternalTextureTests_testMediaSurface`](https://flutter-engine-gold.skia.org/search?corpus=flutter-engine&include_ignored=false&left_filter=name%3DExternalTextureTests_testMediaSurface&max_rgba=0&min_rgba=0&negative=true¬_at_head=false&positive=true&reference_image_required=false&right_filter=&sort=descending&untriaged=true), a [video](app/src/main/assets/sample.mp4) is converted to an external texture and displayed in a Flutter app. The test takes a screenshot of the app and compares it to a golden file: | |
| <img | |
| alt="Two pictures, the top one Flutter and the bottom Android" | |
| src="https://github.com/flutter/flutter/assets/168174/e2c34b88-d03d-4732-87e4-a86c97d006c5" | |
| width="300" | |
| /> | |
| _The top picture is the Flutter app, and the bottom picture is just Android._ | |
| See also: | |
| - [`tools/compare_goldens`](../../../tools/compare_goldens). | |
| ## Prerequisites | |
| If you've never worked in the `flutter/engine` repository before, you will | |
| need to setup a development environment that is quite different from a typical | |
| Flutter app or even working on the Flutter framework. It will take roughly | |
| 30 minutes to an hour to setup an environment, depending on your familiarity | |
| with the tools and the speed of your internet connection. | |
| See also: | |
| - [Setting up the Engine](https://github.com/flutter/flutter/wiki/Setting-up-the-Engine-development-environment) | |
| - [Rebuilding the Tests](#rebuilding-the-tests) | |
| ### Android SDK | |
| It's highly recommended to use the engine's vendored Android SDK, which once | |
| you have the engine set up, you can find at | |
| `$ENGINE/src/flutter/third_party/android_tools/sdk`. Testing or running with | |
| other versions of the SDK may work, but it's _not guaranteed_, and might have | |
| different results. | |
| Consider also placing this directory in the `ANDROID_HOME` environment variable: | |
| ```sh | |
| export ANDROID_HOME=$ENGINE/src/flutter/third_party/android_tools/sdk | |
| ``` | |
| ### Device or Emulator | |
| The tests in this package require a connected device or emulator to run. The | |
| device or emulator should be running the same version of Android as the CI | |
| configuration (there are issues with crashes and other problems on older | |
| emulators in particular). | |
| > [!CAUTION] | |
| > | |
| > [#144561](https://github.com/flutter/flutter/issues/144561): The emulator | |
| > vendored in the engine checkout is old and [has a known issue with Vulkan](https://github.com/flutter/flutter/issues/144232). | |
| > | |
| > If you're working locally, you can update your copy by running: | |
| > | |
| > ```sh | |
| > $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --install emulator | |
| > ``` | |
| ### Additional Dependencies | |
| While not required, it is **strongly recommended** to use an IDE such as | |
| [Android Studio](https://developer.android.com/studio) when contributing to the | |
| Android side of the test suite, as it will provide a better development | |
| experience such as code completion, debugging, and more: | |
| <img | |
| width="500" | |
| alt="Screenshot of Android Studio with Autocompletion" | |
| src="https://github.com/flutter/flutter/assets/168174/79b685f5-8da7-4396-abe6-9baeb29d7ce3" | |
| /> | |
| > [!TIP] | |
| > | |
| > Android Studio is expected to work _out of the box_ in this directory. | |
| > | |
| > If you encounter any issues, please [file a bug](#getting-help). | |
| ## Running the Tests | |
| The [test runner](../bin/run_android_tests.dart) is a Dart script that installs | |
| the test app and test suite on a connected device or emulator, runs the tests, | |
| and reports the results including screenshots (golden files) and logs from | |
| `adb logcat`. | |
| From the `$ENGINE/src/flutter` directory, run: | |
| ```sh | |
| dart ./testing/scenario_app/bin/run_android_tests.dart | |
| ``` | |
| By default when run locally, the test runner: | |
| - Uses the engine-wide default graphics backend for Android. | |
| - Uses the last built Android-specific engine artifacts (i.e. `$ENGINE/src/out/android_*/`). | |
| - Will not diff screenshots, but does save them to a logs directory. | |
| ### Rebuilding the Tests | |
| If you've made changes to any file in `scenario_app`, incluing the Dart code | |
| in `lib/` or the Android code in `android/`, you will need to rebuild the | |
| tests before running them. | |
| To rebuild the `scenario_app` for the `android_debug_unopt_arm64` variant: | |
| ```sh | |
| ninja -C out/android_debug_unopt_arm64 scenario_app | |
| ``` | |
| See also: | |
| - [Compiling the Engine](https://github.com/flutter/flutter/wiki/Compiling-the-engine) | |
| ### Common Options | |
| A list of options can be found by running: | |
| ```sh | |
| dart ./testing/scenario_app/bin/run_android_tests.dart --help | |
| ``` | |
| Frequently used options include: | |
| - `--out-dir`: Which engine artifacts to use (e.g. | |
| `--out-dir=../out/android_debug_unopt_arm64`). | |
| - `--logs-dir`: Where to save full `adb logcat` logs and screenshots. | |
| - `--[no]-enable-impeller`: Enables/disables use of the Impeller graphics | |
| backend. | |
| - `--impeller-backend`: Use a specific Impeller backend (e.g. | |
| `--impeller-backend=opengles`). | |
| - `--force-surface-producer-surface-texture`: Force the use of `SurfaceTexture`s | |
| for plugin code that uses `SurfaceProducer`s. This instruments the same code | |
| path that is used for Android API versions that are <= 28 without requiring | |
| an older emulator or device. | |
| - `--smoke-test=<full.class.Name>`: Runs a specific test, instead of all tests. | |
| ### Advanced Options | |
| When debugging the runner itself, you can use the `--verbose` flag to see more | |
| detailed output, including additional options such as configuring which binary | |
| of `adb` to use: | |
| ```sh | |
| dart ./testing/scenario_app/bin/run_android_tests.dart --help --verbose | |
| ``` | |
| See also: | |
| - [`bin/utils/options.dart`](../bin/utils/options.dart). | |
| ### CI Configuration | |
| See [`ci/builders`](../../../ci/builders) and grep for `run_android_tests.dart`: | |
| ```sh | |
| grep -r run_android_tests.dart ci/builders | |
| ``` | |
| > [!NOTE] | |
| > The Impeller OpenGLES backend tests are only run on staging (`bringup: true`) | |
| > and as such are **non-blocking**. We expect to stabilize and run these tests | |
| > as part of a wider release of the Impeller OpenGLES backend. | |
| #### Older Android | |
| "Older Android" refers to "code paths to support older Android API levels". | |
| Specifically, these configurations use | |
| `--force-surface-producer-surface-texture` (see [above](#common-options) for | |
| details). | |
| | Backend | CI Configuration | CI History | Skia Gold | | |
| | ----------------- | --------------------------- | ---------------------------------------------- | ------------------------- | | |
| | Skia | [`ci/builders`][skia-st-ci] | [Presubmit][skia-try], [Postsubmit][skia-prod] | [Skia Gold][skia-st-gold] | | |
| | Impeller OpenGLES | [`ci/builders`][imp-st-ci] | [Staging][imp-staging] | N/A | | |
| [skia-try]: https://ci.chromium.org/ui/p/flutter/builders/try/Linux%20linux_android_emulator_skia_tests | |
| [skia-prod]: https://ci.chromium.org/ui/p/flutter/builders/prod/Linux%20linux_android_emulator_skia_tests | |
| [imp-staging]: https://ci.chromium.org/ui/p/flutter/builders/staging/Linux%20linux_android_emulator_opengles_tests | |
| [impeller-try]: https://ci.chromium.org/ui/p/flutter/builders/try/Linux%20linux_android_emulator_tests | |
| [impeller-prod]: https://ci.chromium.org/ui/p/flutter/builders/prod/Linux%20linux_android_emulator_tests | |
| [skia-st-ci]: https://github.com/search?q=repo%3Aflutter%2Fengine+path%3Aci%2Fbuilders+%22--no-enable-impeller%22+%22run_android_tests.dart%22+%22--force-surface-producer-surface-texture%22&type=code | |
| [skia-st-gold]: https://flutter-engine-gold.skia.org/search?left_filter=ForceSurfaceProducerSurfaceTexture%3Dtrue%26GraphicsBackend%3Dskia&negative=true&positive=true&right_filter=AndroidAPILevel%3D34%26GraphicsBackend%3Dskia | |
| [imp-st-ci]: https://github.com/search?q=repo%3Aflutter%2Fengine+path%3Aci%2Fbuilders+%22--impeller-backend%3Dopengles%22+%22run_android_tests.dart%22+%22--force-surface-producer-surface-texture%22&type=code | |
| [imp-st-gold]: https://flutter-engine-gold.skia.org/search?left_filter=ForceSurfaceProducerSurfaceTexture%3Dtrue%26GraphicsBackend%3Dimpeller-opengles&negative=true&positive=true&right_filter=AndroidAPILevel%3D34%26GraphicsBackend%3Dskia | |
| #### Newer Android | |
| | Backend | CI Configuration | CI History | Skia Gold | | |
| | ----------------- | ---------------------------- | ------------------------------------------------------ | ------------------------ | | |
| | Skia | [`ci/builders`][skia-ci] | [Presubmit][skia-try], [Postsubmit][skia-prod] | [Skia Gold][skia-gold] | | |
| | Impeller OpenGLES | [`ci/builders`][imp-gles-ci] | [Staging][imp-staging] | N/A | | |
| | Impeller Vulkan | [`ci/builders`][imp-vk-ci] | [Presubmit][impeller-try], [Postsubmit][impeller-prod] | [Skia Gold][imp-vk-gold] | | |
| [skia-ci]: https://github.com/search?q=repo%3Aflutter%2Fengine+path%3Aci%2Fbuilders+%22--no-enable-impeller%22+%22run_android_tests.dart%22&type=code | |
| [skia-gold]: https://flutter-engine-gold.skia.org/search?left_filter=ForceSurfaceProducerSurfaceTexture%3Dfalse%26GraphicsBackend%3Dskia&negative=true&positive=true&right_filter=AndroidAPILevel%3D34%26GraphicsBackend%3Dskia | |
| [imp-gles-ci]: https://github.com/search?q=repo%3Aflutter%2Fengine+path%3Aci%2Fbuilders+%22--impeller-backend%3Dopengles%22+%22run_android_tests.dart%22&type=code | |
| [imp-vk-ci]: https://github.com/search?q=repo%3Aflutter%2Fengine+path%3Aci%2Fbuilders+%22--impeller-backend%3Dvulkan%22+%22run_android_tests.dart%22&type=code | |
| [imp-vk-gold]: https://flutter-engine-gold.skia.org/search?left_filter=ForceSurfaceProducerSurfaceTexture%3Dfalse%26GraphicsBackend%3Dimpeller-vulkan&negative=true&positive=true&right_filter=AndroidAPILevel%3D34%26GraphicsBackend%3Dskia | |
| ## Contributing | |
| [](https://github.com/flutter/flutter/issues?q=is%3Aopen+is%3Aissue+label%3A%22e%3A+scenario-app%22) | |
| Contributions to this package are welcome, as it is a critical part of the | |
| engine's test suite. | |
| ### Anatomy of a Test | |
| A "test" in practice is a combination of roughly 3 components: | |
| 1. An Android _[JUnit test][]_ that configures and launches an Android activity. | |
| 2. An Android _[activity][]_, which simulates the Android side of a plugin or | |
| platform view. | |
| 3. A Dart _[scenario][]_, which simulates the Flutter side of an application. | |
| [junit test]: ./app/src/androidTest/java/dev/flutter/scenariosui/DrawSolidBlueScreenTest.java | |
| [activity]: ./app/src/main/java/dev/flutter/scenarios/ExternalTextureFlutterActivity.java | |
| [scenario]: ../lib/src/solid_blue.dart | |
| While every test suite has exactly one JUnit-instrumented class, each test can | |
| have many activities and scenarios, each with their own configuration, setup, | |
| and assertions. Not all of this is desirable, but it is the current state of | |
| the test suite. | |
| A test might also take a screenshot. See the _Skia Gold_ links in | |
| [CI Configuration](#ci-configuration) for examples. | |
| ### Project History | |
| This test suite was [originally written in 2019](https://github.com/flutter/engine/pull/10007) | |
| with a goal of: | |
| > \[being\] suitable for embedders to do integration testing with - it has no | |
| > dependencies on the flutter_tools or framework, and so will not fail/flake | |
| > based on variances in those downstream. | |
| Unfortunately, the Android side of the test suite was never fully operational, | |
| and the tests, even if failing, were accidentally be reported as passing on CI. | |
| In 2024, as the team got closer to shipping our new graphics backend, | |
| [Impeller](https://docs.flutter.dev/perf/impeller) on Android, it was clear that | |
| we needed a reliable test suite for the engine on Android, particularly for | |
| visual tests around external textures and platform views. | |
| So, this package was revived and updated to be a (more) reliable test suite for | |
| the engine on Android. It's by no means complete | |
| ([contributions welcome](#contributing)), but it did successfully catch at least | |
| one bug that would not have been detected automatically otherwise. | |
| _Go forth and test the engine on Android!_ | |
| ## Troubleshooting | |
| If you encounter any issues, please [file a bug](#getting-help). | |
| ### My test is failing on CI | |
| If a test is failing on CI, it's likely that the test is failing locally as | |
| well. Try the steps in [running the Tests](#running-the-tests) to reproduce the | |
| failure locally, and then debug the failure as you would any other test. If this | |
| is your first time working on the engine, you may need to setup a development | |
| environment first (see [prerequisites](#prerequisites)). | |
| The test runner makes extensive use of logging and screenshots to help debug | |
| failures. If you're not sure where to start, try looking at the logs and | |
| screenshots to see if they provide any clues (you'll need them to file a bug | |
| anyway). | |
| `test: Android Scenario App Integration Tests (Impeller/Vulkan)` | |
| on [LUCI](https://ci.chromium.org/ui/p/flutter/builders/try/Linux%20Engine%20Drone/2079486/overview) produces these logs: | |
|  | |
| The files include a screenshot of each test, and a _full_ logcat log from the | |
| device (you might find it easier trying the `stdout` of the test first, which | |
| uses rudimentary log filtering). In the case of multiple runs, the logs are | |
| prefixed with the test configuration and run attempt. | |
| ### Gradle is failing due to an "not part of the dependency lock state" error | |
| If you update dependencies in the `app/build.gradle` file, you may encounter an | |
| error (probably in CI) that looks like: | |
| ```txt | |
| FAILED: scenario_app/reports/lint-results.xml | |
| vpython3 ../../flutter/testing/rules/run_gradle.py /b/s/w/ir/cache/builder/src/flutter/testing/scenario_app/android lint --no-daemon -Pflutter_jar=/b/s/w/ir/cache/builder/src/out/android_emulator_skia_debug_x64/flutter.jar -Pout_dir=/b/s/w/ir/cache/builder/src/out/android_emulator_skia_debug_x64/scenario_app --project-cache-dir=/b/s/w/ir/cache/builder/src/out/android_emulator_skia_debug_x64/scenario_app/.gradle --gradle-user-home=/b/s/w/ir/cache/builder/src/out/android_emulator_skia_debug_x64/scenario_app/.gradle | |
| FAILURE: Build failed with an exception. | |
| * What went wrong: | |
| Execution failed for task ':app:checkDebugAarMetadata'. | |
| > Could not resolve all files for configuration ':app:debugRuntimeClasspath'. | |
| > Resolved 'org.hamcrest:hamcrest-core:1.3' which is not part of the dependency lock state | |
| > Resolved 'com.google.code.findbugs:jsr305:2.0.2' which is not part of the dependency lock state | |
| ``` | |
| This is because [`gradle.lockfile`](./app/gradle.lockfile) is out of date. To | |
| update it, run: | |
| ```sh | |
| cd testing/scenario_app/android | |
| $ENGINE_SRC/flutter/third_party/gradle/bin/gradle app:dependencies --write-locks | |
| ``` | |
| ## Getting Help | |
| To suggest changes, or highlight problems, please [file an issue](https://github.com/flutter/flutter/issues/new?labels=e:%20scenario-app,engine,platform-android,fyi-android,team-engine). | |
| If you're not sure where to start, or need help debugging or contributing, you | |
| can also reach out to [`hackers-android`](https://discord.com/channels/608014603317936148/846507907876257822) on Discord, or the Flutter engine team internally. There is | |
| no full-time maintainer of this package, so be prepared to do some of the | |
| legwork yourself. |