1# Flutter devicelab 2 3"Devicelab" (a.k.a. "cocoon") is a physical lab that tests Flutter on real 4Android and iOS devices. 5 6This package contains the code for test framework and the tests. More generally 7the tests are referred to as "tasks" in the API, but since we primarily use it 8for testing, this document refers to them as "tests". 9 10Build results are available at https://flutter-dashboard.appspot.com. 11 12# Reading the dashboard 13 14## The build page 15 16The build page is accessible at https://flutter-dashboard.appspot.com/build.html. 17This page reports the health of build servers, called _agents_, and the statuses 18of build tasks. 19 20### Agents 21 22A green agent is considered healthy and ready to receive new tasks to build. A 23red agent is broken and does not receive new tasks. 24 25In the example below, the dashboard shows that the `linux2` agent is broken and 26requires attention. All other agents are healthy. 27 28 29 30### Tasks 31 32The table below the agent statuses displays the statuses of build tasks. Task 33statuses are color-coded. The following statuses are available: 34 35**New task** (light blue): the task is waiting for an agent to pick it up and 36start the build. 37 38**Task is running** (spinning blue): an agent is currently building the task. 39 40**Task succeeded** (green): an agent reported a successful completion of the 41task. 42 43**Task is flaky** (yellow): the task was attempted multiple time, but only the 44latest attempt succeeded (we currently only try twice). 45 46**Task failed** (red): the task failed all of the attempts. 47 48**Task underperformed** (orange): currently not used. 49 50**Task was skipped** (transparent): the task is not scheduled for a build. This 51usually happens when a task is removed from `manifest.yaml` file. 52 53**Task status unknown** (purple): currently not used. 54 55In addition to color-coding, a task may display a question mark. This means 56that the task was marked as flaky manually. The status of such task is ignored 57when considering whether the build is broken or not. For example, if a flaky 58task fails, GitHub will not prevent PR submissions. However, if the latest 59status of a non-flaky task is red, all pending PRs will contain a warning about 60the broken build and recommend caution when submitting. 61 62Legend: 63 64 65 66The example below shows that commit `e122d5d` caused a wide-spread breakage, 67which was fixed by `bdc6f10`. It also shows that Cirrus and Chrome 68Infra (left-most tasks) decided to skip building these commits. Hovering over 69a cell will pop up a tooltip containing the name of the broken task. Clicking 70on the cell will open the log file in a new browser tab (only visible to core 71contributors as of today). 72 73 74 75## Why is a task stuck on "new task" status? 76 77The dashboard aggregates build results from multiple build environments, 78including Cirrus, Chrome Infra, and devicelab. While devicelab 79tests every commit that goes into the `master` branch, other environments 80may skip some commits. For example, Cirrus will only test the 81_last_ commit of a PR that's merged into the `master` branch. Chrome Infra may 82skip commits when they come in too fast. 83 84## How the devicelab runs the tasks 85 86The devicelab agents have a small script installed on them that continuously 87asks the CI server for tasks to run. When the server finds a suitable task for 88an agent it reserves that task for the agent. If the task succeeds, the agent 89reports the success to the server and the dashboard shows that task in green. 90If the task fails, the agent reports the failure to the server, the server 91increments the counter counting the number of attempts it took to run the task 92and puts the task back in the pool of available tasks. If a task does not 93succeed after a certain number of attempts (as of this writing the limit is 2), 94the task is marked as failed and is displayed using red color on the dashboard. 95 96# Running tests locally 97 98Do make sure your tests pass locally before deploying to the CI environment. 99Below is a handful of commands that run tests in a similar way to how the 100CI environment runs them. These commands are also useful when you need to 101reproduce a CI test failure locally. 102 103## Prerequisites 104 105You must set the `ANDROID_HOME` or `ANDROID_SDK_ROOT` environment variable to run 106tests on Android. If you have a local build of the Flutter engine, then you have 107a copy of the Android SDK at `.../engine/src/third_party/android_tools/sdk`. 108 109You can find where your Android SDK is using `flutter doctor`. 110 111## Warnings 112 113Running devicelab will do things to your environment. 114 115Notably, it will start and stop gradle, for instance. 116 117## Running all tests 118 119To run all tests defined in `manifest.yaml`, use option `-a` (`--all`): 120 121```sh 122../../bin/cache/dart-sdk/bin/dart bin/run.dart -a 123``` 124 125## Running specific tests 126 127To run a test, use option `-t` (`--task`): 128 129```sh 130# from the .../flutter/dev/devicelab directory 131../../bin/cache/dart-sdk/bin/dart bin/run.dart -t {NAME_OR_PATH_OF_TEST} 132``` 133 134Where `NAME_OR_PATH_OF_TEST` can be either of: 135 136- the _name_ of a task, which you can find in the `manifest.yaml` file in this 137 directory. Example: `complex_layout__start_up`. 138- the path to a Dart _file_ corresponding to a task, which resides in `bin/tasks`. 139 Tip: most shells support path auto-completion using the Tab key. Example: 140 `bin/tasks/complex_layout__start_up.dart`. 141 142To run multiple tests, repeat option `-t` (`--task`) multiple times: 143 144```sh 145../../bin/cache/dart-sdk/bin/dart bin/run.dart -t test1 -t test2 -t test3 146``` 147 148To run tests from a specific stage, use option `-s` (`--stage`). 149Currently there are only three stages defined, `devicelab`, 150`devicelab_ios` and `devicelab_win`. 151 152 153```sh 154../../bin/cache/dart-sdk/bin/dart bin/run.dart -s {NAME_OF_STAGE} 155``` 156 157## Running tests against a local engine build 158 159To run device lab tests against a local engine build, pass the appropriate 160flags to `bin/run.dart`: 161 162```sh 163../../bin/cache/dart-sdk/bin/dart bin/run.dart --task=[some_task] \ 164 --local-engine-src-path=[path_to_local]/engine/src \ 165 --local-engine=[local_engine_architecture] 166``` 167 168An example of of a local engine architecture is `android_debug_unopt_x86`. 169 170# Reproducing broken builds locally 171 172To reproduce the breakage locally `git checkout` the corresponding Flutter 173revision. Note the name of the test that failed. In the example above the 174failing test is `flutter_gallery__transition_perf`. This name can be passed to 175the `run.dart` command. For example: 176 177```sh 178../../bin/cache/dart-sdk/bin/dart bin/run.dart -t flutter_gallery__transition_perf 179``` 180 181# Writing tests 182 183A test is a simple Dart program that lives under `bin/tests` and uses 184`package:flutter_devicelab/framework/framework.dart` to define and run a _task_. 185 186Example: 187 188```dart 189import 'dart:async'; 190 191import 'package:flutter_devicelab/framework/framework.dart'; 192 193Future<void> main() async { 194 await task(() async { 195 ... do something interesting ... 196 197 // Aggregate results into a JSONable Map structure. 198 Map<String, dynamic> testResults = ...; 199 200 // Report success. 201 return new TaskResult.success(testResults); 202 203 // Or you can also report a failure. 204 return new TaskResult.failure('Something went wrong!'); 205 }); 206} 207``` 208 209Only one `task` is permitted per program. However, that task can run any number 210of tests internally. A task has a name. It succeeds and fails independently of 211other tasks, and is reported to the dashboard independently of other tasks. 212 213A task runs in its own standalone Dart VM and reports results via Dart VM 214service protocol. This ensures that tasks do not interfere with each other and 215lets the CI system time out and clean up tasks that get stuck. 216 217# Adding tests to the CI environment 218 219The `manifest.yaml` file describes a subset of tests we run in the CI. To add 220your test edit `manifest.yaml` and add the following in the "tasks" dictionary: 221 222``` 223 {NAME_OF_TEST}: 224 description: {DESCRIPTION} 225 stage: {STAGE} 226 required_agent_capabilities: {CAPABILITIES} 227``` 228 229Where: 230 231 - `{NAME_OF_TEST}` is the name of your test that also matches the name of the 232 file in `bin/tests` without the `.dart` extension. 233 - `{DESCRIPTION}` is the plain English description of your test that helps 234 others understand what this test is testing. 235 - `{STAGE}` is `devicelab` if you want to run on Android, or `devicelab_ios` if 236 you want to run on iOS. 237 - `{CAPABILITIES}` is an array that lists the capabilities required of 238 the test agent (the computer that runs the test) to run your test. Available 239 capabilities are: `has-android-device`, `has-ios-device`. 240