Clean up test infrastructure (#43030)

See #41880 for history.
diff --git a/.cirrus.yml b/.cirrus.yml
index 9eb90de..3b380f3 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -1,18 +1,41 @@
-container:
-  image: gcr.io/flutter-cirrus/build-flutter-image:latest
+# CIRRUS CONFIGURATION FILE
 
+use_compute_credits: $CIRRUS_USER_COLLABORATOR == 'true'
+
+environment:
+  # For details about environment variables used in Cirrus, including how encrypted variables work,
+  # see https://cirrus-ci.org/guide/writing-tasks/#environment-variables
+  GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb]
+  # We change Flutter's directory to include a space in its name (see $CIRRUS_WORKING_DIR) so that
+  # we constantly test path names with spaces in them. The FLUTTER_SDK_PATH_WITH_SPACE variable must
+  # therefore have a space in it.
+  FLUTTER_SDK_PATH_WITH_SPACE: "flutter sdk"
+  # We force BOT to true so that all our tools know we're in a CI environment. This avoids any
+  # dependency on precisely how Cirrus is detected by our tools.
+  BOT: "true"
+  # Unsetting CIRRUS_CHANGE_MESSAGE and CIRRUS_COMMIT_MESSAGE as they might include non-ASCII
+  # characters which makes Gradle crash. See: https://github.com/flutter/flutter/issues/24935
+  # TODO(amirha): remove once we've migrated to newer Gradle
+  CIRRUS_CHANGE_MESSAGE: ""
+  CIRRUS_COMMIT_MESSAGE: ""
+
+# LINUX SHARDS
 task:
-  use_compute_credits: $CIRRUS_USER_COLLABORATOR == 'true'
-  env:
-    # Name the SDK directory to include a space so that we constantly
-    # test path names with spaces in them.
-    CIRRUS_WORKING_DIR: "/tmp/flutter sdk"
+  container:
+    image: gcr.io/flutter-cirrus/build-flutter-image:latest
+    cpu: $CPU
+    memory: $MEMORY
+  environment:
+    # We shrink our default resource requirement as much as possible because that way we are more
+    # likely to get scheduled. We require 4G of RAM because most of the shards (all but one as of
+    # October 2019) just get OOM-killed with less. Some shards may need more. When increasing the
+    # requirements for select shards, please leave a comment on those shards saying when you
+    # increased the requirements, what numbers you tried, and what the results were.
+    CPU: 1 # 0.1-8 without compute credits, 0.1-30 with (yes, you can go fractional)
+    MEMORY: 4G # 256M-24G without compute credits, 256M-90G with
+    CIRRUS_WORKING_DIR: "/tmp/$FLUTTER_SDK_PATH_WITH_SPACE"
     PATH: "$CIRRUS_WORKING_DIR/bin:$CIRRUS_WORKING_DIR/bin/cache/dart-sdk/bin:$PATH"
     ANDROID_SDK_ROOT: "/opt/android_sdk"
-  git_fetch_script:
-    - git clean -xfd
-    - git fetch origin
-    - git fetch origin master # To set FETCH_HEAD for "git merge-base" to work
   pub_cache:
     folder: $HOME/.pub-cache
     fingerprint_script: echo $OS; grep -r --include=pubspec.yaml 'PUBSPEC CHECKSUM' "$CIRRUS_WORKING_DIR"
@@ -22,345 +45,302 @@
   artifacts_cache:
     folder: bin/cache/artifacts
     fingerprint_script: echo $OS; cat bin/internal/*.version
-  setup_script: ./dev/bots/cirrus_setup.sh
+  setup_script:
+    - date
+    - git clean -xffd
+    - git fetch origin
+    - git fetch origin master # To set FETCH_HEAD, so that "git merge-base" works.
+    - flutter config --no-analytics
+    - flutter doctor -v
+    - flutter update-packages
+    - ./dev/bots/accept_android_sdk_licenses.sh
+    - date
+  on_failure:
+    failure_script:
+      - date
+      - which flutter
   matrix:
-    - name: docs
-      skip: "!changesInclude('dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_drive/**', 'packages/flutter_localizations/**', 'packages/flutter_goldens/**', 'bin/internal/**') && $CIRRUS_BRANCH != 'master'"
-      env:
-        SHARD: docs
+    - name: analyze-linux # linux-only
+      # environment:
+        # Empirically, the analyze-linux shard runs surprisingly fast (under 15 minutes) with just 1
+        # CPU and 4G RAM as of October 2019. It fails with less than 4G though.
+      script:
+        - dart --enable-asserts ./dev/bots/analyze.dart
+
+    - name: framework_tests-widgets-linux
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'bin/internal/**') || $CIRRUS_PR == ''"
+      environment:
+        # We use 3 CPUs because that's the minimum required to get framework_tests-widgets-linux
+        # running fast enough that it is not the long pole, as of OCtober 2019.
+        CPU: 3
+        GOLDCTL: "$CIRRUS_WORKING_DIR/depot_tools/goldctl"
+        GOLD_SERVICE_ACCOUNT: ENCRYPTED[3afeea5ac7201151c3d0dc9648862f0462b5e4f55dc600ca8b692319622f7c3eda3d577b1b16cc2ef0311b7314c1c095]
+      script:
+        - ./dev/bots/download_goldctl.sh
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    - name: framework_tests-libraries-linux
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'bin/internal/**') || $CIRRUS_PR == ''"
+      environment:
+        # We use 3 CPUs because that's the minimum required to get the
+        # framework_tests-libraries-linux shard running fast enough that it is not the long pole, as
+        # of October 2019.
+        CPU: 3
+        GOLDCTL: "$CIRRUS_WORKING_DIR/depot_tools/goldctl"
+        GOLD_SERVICE_ACCOUNT: ENCRYPTED[3afeea5ac7201151c3d0dc9648862f0462b5e4f55dc600ca8b692319622f7c3eda3d577b1b16cc2ef0311b7314c1c095]
+      script:
+        - ./dev/bots/download_goldctl.sh
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    - name: framework_tests-misc-linux
+      # this includes the tests for directories in dev/
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'bin/internal/**') || $CIRRUS_PR == ''"
+      environment:
+        # We use 3 CPUs because that's the minimum required to get framework_tests-misc-linux
+        # running fast enough that it is not the long pole, as of October 2019.
+        CPU: 3
+      script:
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    - name: tool_tests-general-linux
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter_tools/**', 'bin/internal/**') || $CIRRUS_PR == ''"
+      environment:
+        # As of October 2019, the tool_tests-general-linux shard got faster with more CPUs up to 4
+        # CPUs, and needed at least 8G of RAM to not run out of memory.
+        CPU: 4
+        MEMORY: 8G
+      script:
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    - name: tool_tests-commands-linux
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter_tools/**', 'bin/internal/**') || $CIRRUS_PR == ''"
+      environment:
+        # As of October 2019, the tool_tests-commands-linux shard got faster with more CPUs up to 6
+        # CPUs, and needed at least 8G of RAM to not run out of memory.
+        CPU: 6
+        MEMORY: 8G
+      script:
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    - name: tool_tests-integration-linux
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter_tools/**', 'bin/internal/**') || $CIRRUS_PR == ''"
+      environment:
+        # As of October 2019, the tool_tests-integration-linux shard got faster with more CPUs up to
+        # 6 CPUs, and needed at least 8G of RAM to not run out of memory.
+        CPU: 6
+        MEMORY: 8G
+      script:
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    - name: tool_coverage-linux # linux-only
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter_tools/**/*.dart', 'bin/internal/**') || $CIRRUS_PR == ''"
+      environment:
+        # As of October 2019, the tool_coverage-linux shard needed at least 12G of RAM to run without
+        # getting OOM-killed, and even 8 CPUs took 25 minutes.
+        CPU: 8
+        MEMORY: 12G
+        CODECOV_TOKEN: ENCRYPTED[7c76a7f8c9264f3b7f3fd63fcf186f93c62c4dfe43ec288861c2f506d456681032b89efe7b7a139c82156350ca2c752c]
+      script:
+        - dart --enable-asserts ./dev/bots/test.dart
+        - bash <(curl -s https://codecov.io/bash) -c -f packages/flutter_tools/coverage/lcov.info -F flutter_tool
+
+    - name: web_tests-0-linux
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'packages/flutter_web_plugins/**', 'bin/internal/**') || $CIRRUS_PR == ''"
+      environment:
+        # As of October 2019, the Web shards needed more than 6G of RAM.
+        CPU: 2
+        MEMORY: 8G
+      allow_failures: true
+      script:
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    - name: web_tests-1-linux
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'packages/flutter_web_plugins/**', 'bin/internal/**') || $CIRRUS_PR == ''"
+      environment:
+        # As of October 2019, the Web shards needed more than 6G of RAM.
+        CPU: 2
+        MEMORY: 8G
+      allow_failures: true
+      script:
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    - name: web_tests-2-linux
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'packages/flutter_web_plugins/**', 'bin/internal/**') || $CIRRUS_PR == ''"
+      environment:
+        # As of October 2019, the Web shards needed more than 6G of RAM.
+        CPU: 2
+        MEMORY: 8G
+      allow_failures: true
+      script:
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    - name: web_tests-3-linux
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'packages/flutter_web_plugins/**', 'bin/internal/**') || $CIRRUS_PR == ''"
+      environment:
+        # As of October 2019, the Web shards needed more than 6G of RAM.
+        CPU: 2
+        MEMORY: 8G
+      allow_failures: true
+      script:
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    - name: web_tests-4-linux
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'packages/flutter_web_plugins/**', 'bin/internal/**') || $CIRRUS_PR == ''"
+      environment:
+        # As of October 2019, the Web shards needed more than 6G of RAM.
+        CPU: 2
+        MEMORY: 8G
+      allow_failures: true
+      script:
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    - name: web_tests-5_last-linux # last Web shard must end with _last
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'packages/flutter_web_plugins/**', 'bin/internal/**') || $CIRRUS_PR == ''"
+      environment:
+        # As of October 2019, the Web shards needed more than 6G of RAM.
+        CPU: 2
+        MEMORY: 8G
+      allow_failures: true
+      script:
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    - name: build_tests-linux
+      environment:
+        # With 1 CPU and 4G of RAM, as of October 2019, build_tests-linux would get OOM-killed.
+        # Increasing the RAM to 12G allowed it to finish in about 30 minutes, any extra CPU (tried 2
+        # and 4) reduced that to just over 20 minutes. 6G was enough not to get OOM-killed.
+        CPU: 2
+        MEMORY: 6G
+      script:
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    - name: hostonly_devicelab_tests-0-linux
+      environment:
+        # Some of the host-only devicelab tests are pretty involved and need a lot of RAM.
+        CPU: 2
+        MEMORY: 8G
+      script:
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    - name: hostonly_devicelab_tests-1-linux
+      environment:
+        # Some of the host-only devicelab tests are pretty involved and need a lot of RAM.
+        CPU: 2
+        MEMORY: 8G
+      script:
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    - name: hostonly_devicelab_tests-2-linux
+      environment:
+        # Some of the host-only devicelab tests are pretty involved and need a lot of RAM.
+        CPU: 2
+        MEMORY: 8G
+      script:
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    - name: hostonly_devicelab_tests-3-linux
+      environment:
+        # Some of the host-only devicelab tests are pretty involved and need a lot of RAM.
+        CPU: 2
+        MEMORY: 8G
+      script:
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    - name: hostonly_devicelab_tests-4-linux
+      environment:
+        # Some of the host-only devicelab tests are pretty involved and need a lot of RAM.
+        CPU: 2
+        MEMORY: 8G
+      script:
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    - name: hostonly_devicelab_tests-5_last-linux
+      environment:
+        # Some of the host-only devicelab tests are pretty involved and need a lot of RAM.
+        CPU: 2
+        MEMORY: 8G
+      script:
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    # TODO(ianh): name: add_to_app_tests-linux
+
+    - name: docs-linux # linux-only
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_drive/**', 'packages/flutter_localizations/**', 'packages/flutter_goldens/**', 'bin/internal/**') || $CIRRUS_PR == ''"
+      environment:
+        # Empirically, as of October 2019, the docs-linux shard took about 30 minutes when run with
+        # 1 CPU and 4G of RAM. 2 CPUs reduced that to 20 minutes, more CPUs did not improve matters.
+        CPU: 2
         # For uploading master docs to Firebase master branch staging site
         FIREBASE_MASTER_TOKEN: ENCRYPTED[eb768d18798fdc5abfe09b224e1724c4d82831d715ccf90df2c79d618c317216cbd99493278361f6fe7948b409b603f0]
         # For uploading beta docs to Firebase public live site
         FIREBASE_PUBLIC_TOKEN: ENCRYPTED[37e8b82f167864cae9a3f4d2cf3f37dea331d9375c295327c45de524f6c588fa6f6d63e5784f10f6d43ce29689f36c92]
-      docs_script: ./dev/bots/docs.sh
-    - name: deploy_gallery
-      depends_on:
-        - docs
-        - analyze
-        - build_tests-linux
-      env:
-        SHARD: deploy_gallery
-        GOOGLE_DEVELOPER_SERVICE_ACCOUNT_ACTOR_FASTLANE: ENCRYPTED[d9ac1462c3c556fc2f8165c9d5566a16497d8ebc38a50357f7f3abf136b7f83e1d1d76dde36fee356cb0f9ebf7a89346]
-        ANDROID_GALLERY_UPLOAD_KEY: ENCRYPTED[0f2aca35f05b26add5d9edea2a7449341269a2b7e22d5c667f876996e2e8bc44ff1369431ebf73b7c5581fd95d0e5902]
-      test_script:
-        # Unsetting CIRRUS_CHANGE_MESSAGE and CIRRUS_COMMIT_MESSAGE as they
-        # might include non-ASCII characters which makes Gradle crash.
-        # See: https://github.com/flutter/flutter/issues/24935
-        # This is a temporary workaround until we figure how to properly configure
-        # a UTF8 locale on Cirrus (or until the Gradle bug is fixed).
-        # TODO(amirh): Set the locale to UTF8.
-        - echo "$CIRRUS_CHANGE_MESSAGE" > /tmp/cirrus_change_message.txt
-        - echo "$CIRRUS_COMMIT_MESSAGE" > /tmp/cirrus_commit_message.txt
-        - export CIRRUS_CHANGE_MESSAGE=""
-        - export CIRRUS_COMMIT_MESSAGE=""
-        - ./dev/bots/deploy_gallery.sh
-        - export CIRRUS_CHANGE_MESSAGE=`cat /tmp/cirrus_change_message.txt`
-        - export CIRRUS_COMMIT_MESSAGE=`cat /tmp/cirrus_commit_message.txt`
-    - name: analyze
-      test_script:
-        - dart --enable-asserts ./dev/bots/analyze.dart
-    - name: bots_tests-linux
-      skip: "!changesInclude('dev/bots/**')"
-      test_script:
-        - (cd ./dev/bots && pub run test)
-      container:
-        cpu: 4
-        memory: 12G
-    - name: tests_widgets-linux
-      skip: "!changesInclude('dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'bin/internal/**') && $CIRRUS_BRANCH != 'master' && $CIRRUS_BRANCH != 'stable' && $CIRRUS_BRANCH != 'beta' && $CIRRUS_BRANCH != 'dev'"
-      env:
-        GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb]
-        SHARD: tests
-        SUBSHARD: widgets
-        GOLDCTL: "$CIRRUS_WORKING_DIR/depot_tools/goldctl"
-        GOLD_SERVICE_ACCOUNT: ENCRYPTED[3afeea5ac7201151c3d0dc9648862f0462b5e4f55dc600ca8b692319622f7c3eda3d577b1b16cc2ef0311b7314c1c095]
-      goldctl_script: ./dev/bots/download_goldctl.sh
-      test_script:
-        - dart --enable-asserts ./dev/bots/test.dart
-      container:
-        cpu: 4
-        memory: 12G
-    - name: tests_framework_other-linux
-      skip: "!changesInclude('dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'bin/internal/**') && $CIRRUS_BRANCH != 'master' && $CIRRUS_BRANCH != 'stable' && $CIRRUS_BRANCH != 'beta' && $CIRRUS_BRANCH != 'dev'"
-      env:
-        GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb]
-        SHARD: tests
-        SUBSHARD: framework_other
-        GOLDCTL: "$CIRRUS_WORKING_DIR/depot_tools/goldctl"
-        GOLD_SERVICE_ACCOUNT: ENCRYPTED[3afeea5ac7201151c3d0dc9648862f0462b5e4f55dc600ca8b692319622f7c3eda3d577b1b16cc2ef0311b7314c1c095]
-      goldctl_script: ./dev/bots/download_goldctl.sh
-      test_script:
-        - dart --enable-asserts ./dev/bots/test.dart
-      container:
-        cpu: 4
-        memory: 12G
-    - name: tests_extras-linux
-      skip: "!changesInclude('dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'bin/internal/**', 'dev/**') && $CIRRUS_BRANCH != 'master' && $CIRRUS_BRANCH != 'stable' && $CIRRUS_BRANCH != 'beta' && $CIRRUS_BRANCH != 'dev'"
-      env:
-        GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb]
-        SHARD: tests
-        SUBSHARD: extras
-      test_script:
-        - dart --enable-asserts ./dev/bots/test.dart
-      container:
-        cpu: 4
-        memory: 12G
-    # all of the tests except the ones in test/integration and test/commands/create_test for packages/flutter_tools
-    - name: tool_tests_general-linux
-      skip: "!changesInclude('dev/**', 'packages/flutter_tools/**', 'bin/internal/**') && $CIRRUS_BRANCH != 'master' && $CIRRUS_BRANCH != 'stable' && $CIRRUS_BRANCH != 'beta' && $CIRRUS_BRANCH != 'dev'"
-      env:
-        GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb]
-        SHARD: tool_tests
-        SUBSHARD: general
-      test_script:
-        - dart --enable-asserts ./dev/bots/test.dart
-      container:
-        cpu: 4
-        memory: 12G
-    - name: tool_tests_commands-linux
-      skip: "!changesInclude('dev/**', 'packages/flutter_tools/**', 'bin/internal/**') && $CIRRUS_BRANCH != 'master' && $CIRRUS_BRANCH != 'stable' && $CIRRUS_BRANCH != 'beta' && $CIRRUS_BRANCH != 'dev'"
-      env:
-        GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb]
-        SHARD: tool_tests
-        SUBSHARD: commands
-      test_script:
-        - dart --enable-asserts ./dev/bots/test.dart
-      container:
-        cpu: 4
-        memory: 12G
-    # all of the tests in test/integration for packages/flutter_tools
-    - name: tool_tests_integration-linux
-      skip: "!changesInclude('dev/**', 'packages/flutter_tools/**', 'bin/internal/**') && $CIRRUS_BRANCH != 'master' && $CIRRUS_BRANCH != 'stable' && $CIRRUS_BRANCH != 'beta' && $CIRRUS_BRANCH != 'dev'"
-      env:
-        GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb]
-        SHARD: tool_tests
-        SUBSHARD: integration
-      test_script:
-        - dart --enable-asserts ./dev/bots/test.dart
-      container:
-        cpu: 4
-        memory: 12G
-    - name: tool_coverage-linux
-      skip: "!changesInclude('dev/**', 'packages/flutter_tools/**/*.dart') && $CIRRUS_BRANCH != 'master' && $CIRRUS_BRANCH != 'stable' && $CIRRUS_BRANCH != 'beta' && $CIRRUS_BRANCH != 'dev'"
-      env:
-        GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb]
-        CODECOV_TOKEN: ENCRYPTED[7c76a7f8c9264f3b7f3fd63fcf186f93c62c4dfe43ec288861c2f506d456681032b89efe7b7a139c82156350ca2c752c]
-        SHARD: tool_coverage
-      test_script:
-        - dart --enable-asserts ./dev/bots/test.dart
-        - bash <(curl -s https://codecov.io/bash) -c -s ./packages/flutter_tools/coverage/ -f '*.lcov.info' -F flutter_tool
-      container:
-        cpu: 8
-        memory: 24G
-    - name: web_tests-linux-shard-0
-      use_compute_credits: $CIRRUS_USER_COLLABORATOR == 'true'
-      env:
-        SHARD: web_tests
-        WEB_SHARD: 0
-      test_script:
-        - dart --enable-asserts ./dev/bots/test.dart
-      container:
-        cpu: 4
-        memory: 12G
-    - name: web_tests-linux-shard-1
-      use_compute_credits: $CIRRUS_USER_COLLABORATOR == 'true'
-      env:
-        SHARD: web_tests
-        WEB_SHARD: 1
-      test_script:
-        - dart --enable-asserts ./dev/bots/test.dart
-      container:
-        cpu: 4
-        memory: 12G
-    - name: web_tests-linux-shard-2
-      use_compute_credits: $CIRRUS_USER_COLLABORATOR == 'true'
-      env:
-        SHARD: web_tests
-        WEB_SHARD: 2
-      test_script:
-        - dart --enable-asserts ./dev/bots/test.dart
-      container:
-        cpu: 4
-        memory: 12G
-    - name: web_tests-linux-shard-3
-      use_compute_credits: $CIRRUS_USER_COLLABORATOR == 'true'
-      env:
-        SHARD: web_tests
-        WEB_SHARD: 3
-      test_script:
-        - dart --enable-asserts ./dev/bots/test.dart
-      container:
-        cpu: 4
-        memory: 12G
-    - name: web_tests-linux-shard-4
-      use_compute_credits: $CIRRUS_USER_COLLABORATOR == 'true'
-      env:
-        SHARD: web_tests
-        WEB_SHARD: 4
-      test_script:
-        - dart --enable-asserts ./dev/bots/test.dart
-      container:
-        cpu: 4
-        memory: 12G
-    - name: web_tests-linux-shard-5
-      use_compute_credits: $CIRRUS_USER_COLLABORATOR == 'true'
-      env:
-        SHARD: web_tests
-        WEB_SHARD: 5
-      test_script:
-        - dart --enable-asserts ./dev/bots/test.dart
-      container:
-        cpu: 4
-        memory: 12G
-    - name: build_tests-linux
-      env:
-        SHARD: build_tests
-    - name: integration_tests-linux
-      env:
-        SHARD: integration_tests
-      test_script:
-        # Unsetting CIRRUS_CHANGE_MESSAGE and CIRRUS_COMMIT_MESSAGE as they
-        # might include non-ASCII characters which makes Gradle crash.
-        # See: https://github.com/flutter/flutter/issues/24935
-        # This is a temporary workaround until we figure how to properly configure
-        # a UTF8 locale on Cirrus (or until the Gradle bug is fixed).
-        # TODO(amirh): Set the locale to UTF8.
-        - echo "$CIRRUS_CHANGE_MESSAGE" > /tmp/cirrus_change_message.txt
-        - echo "$CIRRUS_COMMIT_MESSAGE" > /tmp/cirrus_commit_message.txt
-        - export CIRRUS_CHANGE_MESSAGE=""
-        - export CIRRUS_COMMIT_MESSAGE=""
-        - dart --enable-asserts ./dev/bots/test.dart
-        - export CIRRUS_CHANGE_MESSAGE=`cat /tmp/cirrus_change_message.txt`
-        - export CIRRUS_COMMIT_MESSAGE=`cat /tmp/cirrus_commit_message.txt`
-      container:
-        cpu: 4
-        memory: 12G
-    - name: gradle_tests-linux-shard-1
-      env:
-        SHARD: integration_tests
-        SUBSHARD: gradle1
-      test_script:
-        - dart --enable-asserts ./dev/bots/test.dart
-      container:
-        cpu: 4
-        memory: 12G
-    - name: gradle_tests-linux-shard-2
-      env:
-        SHARD: integration_tests
-        SUBSHARD: gradle2
-      test_script:
-        - dart --enable-asserts ./dev/bots/test.dart
-      container:
-        cpu: 4
-        memory: 12G
-    - name: gradle_embedding_v2_tests-linux-shard-1
-      env:
-        SHARD: integration_tests
-        SUBSHARD: gradle1
-        ENABLE_ANDROID_EMBEDDING_V2: 'true'
-      test_script:
-        - dart --enable-asserts ./dev/bots/test.dart
-      container:
-        cpu: 4
-        memory: 12G
-    - name: gradle_embedding_v2_tests-linux-shard-2
-      env:
-        SHARD: integration_tests
-        SUBSHARD: gradle2
-        ENABLE_ANDROID_EMBEDDING_V2: 'true'
-      test_script:
-        - dart --enable-asserts ./dev/bots/test.dart
-      container:
-        cpu: 4
-        memory: 12G
-    - name: release_smoke_tests
-      env:
-        CLOUDSDK_CORE_DISABLE_PROMPTS: 1
-        GCLOUD_FIREBASE_TESTLAB_KEY: ENCRYPTED[1c140257edc48f5578fa5a0e5038b84c8e53270c405efa5a8e35ea303a4e0d135853989f448f72136206de854d17fbec]
-      test_script:
-        - echo "$CIRRUS_CHANGE_MESSAGE" > /tmp/cirrus_change_message.txt
-        - echo "$CIRRUS_COMMIT_MESSAGE" > /tmp/cirrus_commit_message.txt
-        - export CIRRUS_CHANGE_MESSAGE=""
-        - export CIRRUS_COMMIT_MESSAGE=""
-        - ./dev/bots/firebase_testlab.sh
-        - export CIRRUS_CHANGE_MESSAGE=`cat /tmp/cirrus_change_message.txt`
-        - export CIRRUS_COMMIT_MESSAGE=`cat /tmp/cirrus_commit_message.txt`
+      script:
+        - ./dev/bots/docs.sh
+
     - name: customer_testing-linux
+      # environment:
+        # Empirically, this shard runs fine at 1 CPU and 4G RAM as of October 2019. We will probably
+        # want to grow this container when we invite people to add their tests in large numbers.
       script:
         - rm -rf bin/cache/pkg/tests
         - git clone https://github.com/flutter/tests.git bin/cache/pkg/tests
         - dart --enable-asserts dev/customer_testing/run_tests.dart --skip-on-fetch-failure --skip-template bin/cache/pkg/tests/registry/*.test
 
-task:
-  use_compute_credits: $CIRRUS_USER_COLLABORATOR == 'true'
-  windows_container:
-    image: cirrusci/android-sdk:28-windowsservercore-2019
-    os_version: 2019
-    cpu: 4
-  env:
-    CIRRUS_WORKING_DIR: "C:\\Windows\\Temp\\flutter sdk"
-    PATH: "$CIRRUS_WORKING_DIR/bin;$CIRRUS_WORKING_DIR/bin/cache/dart-sdk/bin;$PATH"
-  git_fetch_script:
-    - git clean -xfd
-    - git fetch origin
-    - git fetch origin master # To set FETCH_HEAD for "git merge-base" to work
-  pub_cache:
-    folder: $APPDATA\Pub\Cache
-    fingerprint_script:
-      - ps:  $Env:OS; Get-ChildItem -Path "$Env:CIRRUS_WORKING_DIR" pubspec.yaml -Recurse | Select-String -Pattern "PUBSPEC CHECKSUM" -SimpleMatch
-  flutter_pkg_cache:
-    folder: bin\cache\pkg
-    fingerprint_script: echo %OS% & type bin\internal\*.version
-  artifacts_cache:
-    folder: bin\cache\artifacts
-    fingerprint_script: echo %OS% & type bin\internal\engine.version
-  setup_script:
-    - flutter config --no-analytics
-    - flutter doctor -v
-    - flutter update-packages
-    - git fetch origin master
-  test_all_script:
-    - dart --enable-asserts dev\bots\test.dart
-  matrix:
-    # all of the tests except test/integration and test/commands/create_test for packages/flutter_tools
-    - name: tool_tests_general-windows
-      skip: "!changesInclude('dev/**', 'packages/flutter_tools/**', 'bin/internal/**') && $CIRRUS_BRANCH != 'master' && $CIRRUS_BRANCH != 'stable' && $CIRRUS_BRANCH != 'beta' && $CIRRUS_BRANCH != 'dev'"
-      env:
-        GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb]
-        SHARD: tool_tests
-        SUBSHARD: general
-    # all of the tests in test/commands/create_test
-    - name: tool_tests_commands-windows
-      skip: "!changesInclude('dev/**', 'packages/flutter_tools/**', 'bin/internal/**') && $CIRRUS_BRANCH != 'master' && $CIRRUS_BRANCH != 'stable' && $CIRRUS_BRANCH != 'beta' && $CIRRUS_BRANCH != 'dev'"
-      env:
-        GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb]
-        SHARD: tool_tests
-        SUBSHARD: commands
-    # all of the tests in test/integration for packages/flutter_tools
-    - name: tool_tests_integration-windows
-      skip: "!changesInclude('dev/**', 'packages/flutter_tools/**', 'bin/internal/**') && $CIRRUS_BRANCH != 'master' && $CIRRUS_BRANCH != 'stable' && $CIRRUS_BRANCH != 'beta' && $CIRRUS_BRANCH != 'dev'"
-      env:
-        GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb]
-        SHARD: tool_tests
-        SUBSHARD: integration
+    - name: firebase_test_lab_tests-linux # linux-only
+      environment:
+        # Empirically, this shard runs in 20-25 minutes with just one CPU and 4G of RAM, as of
+        # October 2019. It does not seem to be sensitive to the number of CPUs or amount of RAM;
+        # doubling CPUs had no effect (mere seconds under 20 minutes), increasing RAM to 24G left it
+        # on the high end of the 20-25 minute range. (This makes sense, as it's just driving the
+        # Firebase test lab remotely.) Less than 4G of RAM made it go OOM.
+        CLOUDSDK_CORE_DISABLE_PROMPTS: 1
+        GCLOUD_FIREBASE_TESTLAB_KEY: ENCRYPTED[1c140257edc48f5578fa5a0e5038b84c8e53270c405efa5a8e35ea303a4e0d135853989f448f72136206de854d17fbec]
+      script:
+        - ./dev/bots/firebase_testlab.sh
 
+    - name: deploy_gallery-linux # linux- and macos- only
+      depends_on:
+        - analyze-linux
+        - framework_tests-widgets-linux
+        - framework_tests-libraries-linux
+        - framework_tests-misc-linux
+        - tool_tests-general-linux
+        - tool_tests-commands-linux
+        - tool_tests-integration-linux
+        - build_tests-linux
+        - hostonly_devicelab_tests-0-linux
+        - hostonly_devicelab_tests-1-linux
+        - hostonly_devicelab_tests-2-linux
+        - hostonly_devicelab_tests-3-linux
+        - hostonly_devicelab_tests-4-linux
+        - hostonly_devicelab_tests-5_last-linux
+        - firebase_test_lab_tests-linux
+      environment:
+        # As of October 2019, 1 CPU and 4G of RAM let deploy_gallery-linux finish in about 15
+        # minutes, once it got started.
+        GOOGLE_DEVELOPER_SERVICE_ACCOUNT_ACTOR_FASTLANE: ENCRYPTED[d9ac1462c3c556fc2f8165c9d5566a16497d8ebc38a50357f7f3abf136b7f83e1d1d76dde36fee356cb0f9ebf7a89346]
+        ANDROID_GALLERY_UPLOAD_KEY: ENCRYPTED[0f2aca35f05b26add5d9edea2a7449341269a2b7e22d5c667f876996e2e8bc44ff1369431ebf73b7c5581fd95d0e5902]
+      script:
+        - ./dev/bots/deploy_gallery.sh
+
+# WINDOWS SHARDS
 task:
-  use_compute_credits: $CIRRUS_USER_COLLABORATOR == 'true'
   windows_container:
     image: cirrusci/android-sdk:28-windowsservercore-2019
     os_version: 2019
-    cpu: 4
-  env:
-    CIRRUS_WORKING_DIR: "C:\\Windows\\Temp\\flutter sdk"
+    cpu: $CPU
+    memory: $MEMORY
+  environment:
+    # It's not clear that changing the cpu/memory fields here has any impact, so we use
+    # small numbers...
+    CPU: 2 # 1-8 without compute credits, 1-30 with
+    MEMORY: 2G # 256M-24G without compute credits, 256M-90G with
+    CIRRUS_WORKING_DIR: "C:\\Windows\\Temp\\$FLUTTER_SDK_PATH_WITH_SPACE"
     PATH: "$CIRRUS_WORKING_DIR/bin;$CIRRUS_WORKING_DIR/bin/cache/dart-sdk/bin;$PATH"
-  git_fetch_script:
-    - git clean -xfd
-    - git fetch origin
-    - git fetch origin master # To set FETCH_HEAD for "git merge-base" to work
   pub_cache:
     folder: $APPDATA\Pub\Cache
     fingerprint_script:
-      - ps:  $Env:OS; Get-ChildItem -Path "$Env:CIRRUS_WORKING_DIR" pubspec.yaml -Recurse | Select-String -Pattern "PUBSPEC CHECKSUM" -SimpleMatch
+      - ps: $Environment:OS; Get-ChildItem -Path "$Environment:CIRRUS_WORKING_DIR" pubspec.yaml -Recurse | Select-String -Pattern "PUBSPEC CHECKSUM" -SimpleMatch
   flutter_pkg_cache:
     folder: bin\cache\pkg
     fingerprint_script: echo %OS% & type bin\internal\*.version
@@ -368,290 +348,285 @@
     folder: bin\cache\artifacts
     fingerprint_script: echo %OS% & type bin\internal\*.version
   setup_script:
+    - git clean -xffd
+    - git fetch origin
+    - git fetch origin master # To set FETCH_HEAD, so that "git merge-base" works.
     - flutter config --no-analytics
     - flutter doctor -v
     - flutter update-packages
     - git fetch origin master
   matrix:
-    - name: bots_tests-windows
-      skip: "!changesInclude('dev/bots/**')"
-      test_script:
-        - cd dev\bots
-        - pub run test
-    - name: tests_widgets-windows
-      skip: "!changesInclude('dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'bin/internal/**') && $CIRRUS_BRANCH != 'master' && $CIRRUS_BRANCH != 'stable' && $CIRRUS_BRANCH != 'beta' && $CIRRUS_BRANCH != 'dev'"
-      env:
-        GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb]
-        SHARD: tests
-        SUBSHARD: widgets
+    - name: framework_tests-widgets-windows
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'bin/internal/**') || $CIRRUS_PR == ''"
+      environment:
         GOLDCTL: "C:\\Windows\\Temp\\depot_tools\\goldctl.exe"
         GOLD_SERVICE_ACCOUNT: ENCRYPTED[3afeea5ac7201151c3d0dc9648862f0462b5e4f55dc600ca8b692319622f7c3eda3d577b1b16cc2ef0311b7314c1c095]
-      goldctl_script: powershell dev\bots\download_goldctl.ps1
-      test_all_script:
+      script:
+        - powershell dev\bots\download_goldctl.ps1
         - dart --enable-asserts dev\bots\test.dart
-    - name: tests_framework_other-windows
-      skip: "!changesInclude('dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'bin/internal/**') && $CIRRUS_BRANCH != 'master' && $CIRRUS_BRANCH != 'stable' && $CIRRUS_BRANCH != 'beta' && $CIRRUS_BRANCH != 'dev'"
-      env:
-        GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb]
-        SHARD: tests
-        SUBSHARD: framework_other
+
+    - name: framework_tests-libraries-windows
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'bin/internal/**') || $CIRRUS_PR == ''"
+      environment:
         GOLDCTL: "C:\\Windows\\Temp\\depot_tools\\goldctl.exe"
         GOLD_SERVICE_ACCOUNT: ENCRYPTED[3afeea5ac7201151c3d0dc9648862f0462b5e4f55dc600ca8b692319622f7c3eda3d577b1b16cc2ef0311b7314c1c095]
-      goldctl_script: powershell dev\bots\download_goldctl.ps1
-      test_all_script:
+      script:
+        - powershell dev\bots\download_goldctl.ps1
         - dart --enable-asserts dev\bots\test.dart
-    - name: tests_extras-windows
-      skip: "!changesInclude('dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'bin/internal/**', 'dev/**') && $CIRRUS_BRANCH != 'master' && $CIRRUS_BRANCH != 'stable' && $CIRRUS_BRANCH != 'beta' && $CIRRUS_BRANCH != 'dev'"
-      env:
-        GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb]
-        SHARD: tests
-        SUBSHARD: extras
-      test_all_script:
+
+    - name: framework_tests-misc-windows
+      # this includes the tests for directories in dev/
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'bin/internal/**') || $CIRRUS_PR == ''"
+      script:
         - dart --enable-asserts dev\bots\test.dart
+
+    - name: tool_tests-general-windows
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter_tools/**', 'bin/internal/**') || $CIRRUS_PR == ''"
+      script:
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    - name: tool_tests-commands-windows
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter_tools/**', 'bin/internal/**') || $CIRRUS_PR == ''"
+      script:
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    - name: tool_tests-integration-windows
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter_tools/**', 'bin/internal/**') || $CIRRUS_PR == ''"
+      script:
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    # TODO(ianh): Enable Web tests on Windows
+
     - name: build_tests-windows
-      env:
-        SHARD: build_tests
-      container:
-        cpu: 4
-        memory: 12G
-      test_all_script:
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'bin/internal/**') || $CIRRUS_PR == ''" # https://github.com/flutter/flutter/issues/41941
+      script:
         - dart --enable-asserts dev\bots\test.dart
-    - name: integration_tests-windows
-      env:
-        SHARD: integration_tests
-      container:
-        cpu: 4
-        memory: 12G
-      test_all_script:
-        - dart --enable-asserts dev\bots\test.dart
+
+    - name: hostonly_devicelab_tests-0-windows
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'bin/internal/**') || $CIRRUS_PR == ''" # https://github.com/flutter/flutter/issues/41941
+      script:
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    - name: hostonly_devicelab_tests-1-windows
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'bin/internal/**') || $CIRRUS_PR == ''" # https://github.com/flutter/flutter/issues/41941
+      script:
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    - name: hostonly_devicelab_tests-2-windows
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'bin/internal/**') || $CIRRUS_PR == ''" # https://github.com/flutter/flutter/issues/41941
+      script:
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    - name: hostonly_devicelab_tests-3-windows
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'bin/internal/**') || $CIRRUS_PR == ''" # https://github.com/flutter/flutter/issues/41941
+      script:
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    - name: hostonly_devicelab_tests-4-windows
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'bin/internal/**') || $CIRRUS_PR == ''" # https://github.com/flutter/flutter/issues/41941
+      script:
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    - name: hostonly_devicelab_tests-5_last-windows
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'bin/internal/**') || $CIRRUS_PR == ''" # https://github.com/flutter/flutter/issues/41941
+      script:
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    # TODO(ianh): name: add_to_app_tests-windows
+
     - name: customer_testing-windows
-      test_script:
+      script:
         - CMD /S /C "IF EXIST "bin\cache\pkg\tests\" RMDIR /S /Q bin\cache\pkg\tests"
         - git clone https://github.com/flutter/tests.git bin\cache\pkg\tests
         - dart --enable-asserts dev\customer_testing\run_tests.dart --skip-on-fetch-failure --skip-template bin/cache/pkg/tests/registry/*.test
-    - name: gradle_tests-windows-shard-1
-      env:
-        SHARD: integration_tests
-        SUBSHARD: gradle1
-      test_script:
-        - dart --enable-asserts dev\bots\test.dart
-      container:
-        cpu: 4
-        memory: 12G
-    - name: gradle_tests-windows-shard-2
-      env:
-        SHARD: integration_tests
-        SUBSHARD: gradle2
-      test_script:
-        - dart --enable-asserts dev\bots\test.dart
-      container:
-        cpu: 4
-        memory: 12G
-    - name: gradle_embedding_v2_tests-windows-shard-1
-      env:
-        SHARD: integration_tests
-        SUBSHARD: gradle1
-        ENABLE_ANDROID_EMBEDDING_V2: 'true'
-      test_script:
-        - dart --enable-asserts dev\bots\test.dart
-      container:
-        cpu: 4
-        memory: 12G
-    - name: gradle_embedding_v2_tests-windows-shard-2
-      env:
-        SHARD: integration_tests
-        SUBSHARD: gradle2
-        ENABLE_ANDROID_EMBEDDING_V2: 'true'
-      test_script:
-        - dart --enable-asserts dev\bots\test.dart
-      container:
-        cpu: 4
-        memory: 12G
 
+# MACOS SHARDS
+# Mac doesn't use caches because they apparently take longer to populate and save
+# than just fetching the data in the first place.
 task:
-  use_compute_credits: $CIRRUS_USER_COLLABORATOR == 'true'
-  name: deploy_gallery-macos
-  depends_on:
-    - analyze
-  env:
-    # Name the SDK directory to include a space so that we constantly
-    # test path names with spaces in them.
-    CIRRUS_WORKING_DIR: "/tmp/flutter sdk"
-    SHARD: deploy_gallery
-    # Apple Fastlane password.
-    FASTLANE_PASSWORD: ENCRYPTED[4b1f0b8d52874e9de965acd46c79743f3b81f3a513614179b9be7cf53dc8258753e257bdadb11a298ee455259df21865]
-    # Private repo for publishing certificates.
-    PUBLISHING_MATCH_CERTIFICATE_REPO: ENCRYPTED[3c0e78877d933fc80107aa6f3790fd1cf927250b852d6cb53202be696b4903ed8ca839b809626aaf18050bf7e436fab7]
-    PUBLISHING_MATCH_REPO_TOKEN: ENCRYPTED[3d1230b744c6ed6c788a91bec741b769401dbcd426b18f9af8080bfeefdfc21913ca4047980c5b5b7ce823f12e7b6b19]
-    # Apple Certificates Match Passphrase
-    MATCH_PASSWORD: ENCRYPTED[db07f252234397090e3ec59152d9ec1831f5ecd0ef97d247b1dca757bbb9ef9b7c832a39bce2caf1949ccdf097e59a73]
   osx_instance:
-    image: mojave-xcode-10.2
-  # occasionally the clock on these machines is out of sync
-  # with the actual time - this should help to verify
-  print_date_script:
-    - date
-  install_cocoapods_script:
-    - sudo gem install cocoapods --no-document
-  git_fetch_script:
-    - git clean -xfd
-    - git fetch origin
-    - git fetch origin master # To set FETCH_HEAD
-  setup_script:
-    - bin/flutter config --no-analytics
-    - bin/flutter update-packages
-  test_all_script:
-    - ./dev/bots/deploy_gallery.sh
-
-task:
-  use_compute_credits: $CIRRUS_USER_COLLABORATOR == 'true'
-  osx_instance:
-    image: mojave-xcode-10.2
-  depends_on:
-    - analyze
-  env:
-    CIRRUS_WORKING_DIR: "/tmp/flutter sdk"
-    COCOAPODS_DISABLE_STATS: true
-  print_date_script:
-    - date
-  git_fetch_script:
-    - git clean -xfd
-    - git fetch origin
-    - git fetch origin master # To set FETCH_HEAD for "git merge-base" to work
-  setup_script:
-    - bin/flutter config --no-analytics
-    - bin/flutter doctor -v
-    - bin/flutter update-packages
-  test_all_script:
-    - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976
-    - bin/cache/dart-sdk/bin/dart --enable-asserts dev/bots/test.dart
-  matrix:
-    # all of the tests except test/integration and test/commands/create_test for packages/flutter_tools
-    - name: tool_tests_general-macos
-      skip: "!changesInclude('dev/**', 'packages/flutter_tools/**', 'bin/internal/**') && $CIRRUS_BRANCH != 'master' && $CIRRUS_BRANCH != 'stable' && $CIRRUS_BRANCH != 'beta' && $CIRRUS_BRANCH != 'dev'"
-      env:
-        GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb]
-        SHARD: tool_tests
-        SUBSHARD: general
-    # all of the tests in test/commands/create_test
-    - name: tool_tests_commands-macos
-      skip: "!changesInclude('dev/**', 'packages/flutter_tools/**', 'bin/internal/**') && $CIRRUS_BRANCH != 'master' && $CIRRUS_BRANCH != 'stable' && $CIRRUS_BRANCH != 'beta' && $CIRRUS_BRANCH != 'dev'"
-      env:
-        GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb]
-        SHARD: tool_tests
-        SUBSHARD: commands
-    # all of the tests in test/integration for packages/flutter_tools
-    - name: tool_tests_integration-macos
-      only_if: $CIRRUS_BRANCH == 'master'
-      env:
-        GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb]
-        SHARD: tool_tests
-        SUBSHARD: integration
-
-task:
-  use_compute_credits: $CIRRUS_USER_COLLABORATOR == 'true'
-  osx_instance:
-    image: mojave-xcode-10.2
-  depends_on:
-    - analyze
-  env:
-    CIRRUS_WORKING_DIR: "/tmp/flutter sdk"
-    COCOAPODS_DISABLE_STATS: true
+    image: mojave-xcode-10.2-flutter # see https://cirrus-ci.org/guide/macOS/ for list of images (we should update regularly)
+    # cpu is always 2
+    # memory is always 8G
+  environment:
+    CIRRUS_WORKING_DIR: "/tmp/$FLUTTER_SDK_PATH_WITH_SPACE"
+    FLUTTER_FRAMEWORK_DIR: "$CIRRUS_WORKING_DIR/bin/cache/artifacts/engine/ios/"
     PATH: "$CIRRUS_WORKING_DIR/bin:$CIRRUS_WORKING_DIR/bin/cache/dart-sdk/bin:$PATH"
-  # occasionally the clock on these machines is out of sync
-  # with the actual time - this should help to verify
-  print_date_script:
-    - date
-  install_cocoapods_script:
-    - sudo gem install cocoapods --no-document
-  git_fetch_script:
-    - git clean -xfd
-    - git fetch origin
-    - git fetch origin master # To set FETCH_HEAD for "git merge-base" to work
+    COCOAPODS_DISABLE_STATS: true
+    CPU: 2
+    MEMORY: 8G
   setup_script:
-    - bin/flutter config --no-analytics
-    - bin/flutter doctor -v
-    - bin/flutter update-packages
+    - date
+    - which flutter
+    - sudo gem install cocoapods
+    - sudo gem install xcpretty
+    - git clean -xffd
+    - git fetch origin
+    - git fetch origin master # To set FETCH_HEAD, so that "git merge-base" works.
+    - flutter config --no-analytics
+    - flutter doctor -v
+    - flutter update-packages
+    - date
+    - which flutter
+  on_failure:
+    failure_script:
+      - date
+      - which flutter
   matrix:
-    - name: bots_tests-macos
-      skip: "!changesInclude('dev/bots/**')"
-      test_script:
-        - (cd ./dev/bots && pub run test)
-    - name: tests_widgets-macos
-      only_if: $CIRRUS_BRANCH == 'master'
-      env:
-        GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb]
-        SHARD: tests
-        SUBSHARD: widgets
+    - name: framework_tests-widgets-macos
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'bin/internal/**') || $CIRRUS_PR == ''"
+      environment:
         GOLDCTL: "$CIRRUS_WORKING_DIR/depot_tools/goldctl"
         GOLD_SERVICE_ACCOUNT: ENCRYPTED[3afeea5ac7201151c3d0dc9648862f0462b5e4f55dc600ca8b692319622f7c3eda3d577b1b16cc2ef0311b7314c1c095]
-      goldctl_script: ./dev/bots/download_goldctl.sh
-      test_all_script:
+      script:
         - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976
+        - ./dev/bots/download_goldctl.sh
         - dart --enable-asserts dev/bots/test.dart
-      on_failure:
-        print_failure_time_script: date
-    - name: tests_framework_other-macos
-      only_if: $CIRRUS_BRANCH == 'master'
-      env:
-        GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb]
-        SHARD: tests
-        SUBSHARD: framework_other
+
+    - name: framework_tests-libraries-macos
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'bin/internal/**') || $CIRRUS_PR == ''"
+      environment:
         GOLDCTL: "$CIRRUS_WORKING_DIR/depot_tools/goldctl"
         GOLD_SERVICE_ACCOUNT: ENCRYPTED[3afeea5ac7201151c3d0dc9648862f0462b5e4f55dc600ca8b692319622f7c3eda3d577b1b16cc2ef0311b7314c1c095]
-      goldctl_script: ./dev/bots/download_goldctl.sh
-      test_all_script:
+      script:
+        - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976
+        - ./dev/bots/download_goldctl.sh
+        - dart --enable-asserts dev/bots/test.dart
+
+    - name: framework_tests-misc-macos
+      # this includes the tests for directories in dev/
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'bin/internal/**') || $CIRRUS_PR == ''"
+      script:
         - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976
         - dart --enable-asserts dev/bots/test.dart
-      on_failure:
-        print_failure_time_script: date
-    - name: integration_tests-macos
-      only_if: $CIRRUS_BRANCH == 'master'
-      env:
-        SHARD: integration_tests
-      test_all_script:
+
+    - name: tool_tests-general-macos
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter_tools/**', 'bin/internal/**') || $CIRRUS_PR == ''"
+      script:
+        - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    - name: tool_tests-commands-macos
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter_tools/**', 'bin/internal/**') || $CIRRUS_PR == ''"
+      script:
+        - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    - name: tool_tests-integration-macos
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter_tools/**', 'bin/internal/**') || $CIRRUS_PR == ''"
+      script:
+        - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    # TODO(ianh): Enable Web tests on macOS.
+
+    - name: build_tests-macos
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'bin/internal/**') || $CIRRUS_PR == ''" # https://github.com/flutter/flutter/issues/41940
+      script:
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    - name: hostonly_devicelab_tests-0-macos
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'bin/internal/**') || $CIRRUS_PR == ''" # https://github.com/flutter/flutter/issues/41940
+      script:
+        - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    - name: hostonly_devicelab_tests-1-macos
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'bin/internal/**') || $CIRRUS_PR == ''" # https://github.com/flutter/flutter/issues/41940
+      script:
+        - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    - name: hostonly_devicelab_tests-2-macos
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'bin/internal/**') || $CIRRUS_PR == ''" # https://github.com/flutter/flutter/issues/41940
+      script:
+        - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    - name: hostonly_devicelab_tests-3-macos
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'bin/internal/**') || $CIRRUS_PR == ''" # https://github.com/flutter/flutter/issues/41940
+      script:
+        - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    - name: hostonly_devicelab_tests-4-macos
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'bin/internal/**') || $CIRRUS_PR == ''" # https://github.com/flutter/flutter/issues/41940
+      script:
+        - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    - name: hostonly_devicelab_tests-5_last-macos
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'bin/internal/**') || $CIRRUS_PR == ''" # https://github.com/flutter/flutter/issues/41940
+      script:
+        - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976
+        - dart --enable-asserts ./dev/bots/test.dart
+
+    - name: add_to_app_tests-macos
+      only_if: "changesInclude('.cirrus.yml', 'dev/**', 'bin/internal/**') || $CIRRUS_PR == ''" # https://github.com/flutter/flutter/issues/41940
+      skip: true # https://github.com/flutter/flutter/pull/42444
+      script:
         - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976
         - dart --enable-asserts dev/bots/test.dart
-    - name: add2app-macos
-      skip: true # https://github.com/flutter/flutter/issues/39507
-      env:
-        SHARD: add2app_test
-      setup_xcpretty_script:
-        - sudo gem install xcpretty
-      test_all_script:
-        - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976
-        - dart --enable-asserts dev/bots/test.dart
+
     - name: customer_testing-macos
-      test_script:
+      script:
+        - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976
         - rm -rf bin/cache/pkg/tests
         - git clone https://github.com/flutter/tests.git bin/cache/pkg/tests
         - dart --enable-asserts dev/customer_testing/run_tests.dart --skip-on-fetch-failure --skip-template bin/cache/pkg/tests/registry/*.test
 
+    - name: deploy_gallery-macos # linux- and macos- only
+      depends_on:
+        - analyze-linux
+        - framework_tests-widgets-macos
+        - framework_tests-libraries-macos
+        - framework_tests-misc-macos
+        - tool_tests-general-macos
+        - tool_tests-commands-macos
+        - tool_tests-integration-macos
+        - build_tests-macos
+        - hostonly_devicelab_tests-0-macos
+        - hostonly_devicelab_tests-1-macos
+        - hostonly_devicelab_tests-2-macos
+        - hostonly_devicelab_tests-3-macos
+        - hostonly_devicelab_tests-4-macos
+        - hostonly_devicelab_tests-5_last-macos
+        - firebase_test_lab_tests-linux
+      environment:
+        # Apple Fastlane password.
+        FASTLANE_PASSWORD: ENCRYPTED[4b1f0b8d52874e9de965acd46c79743f3b81f3a513614179b9be7cf53dc8258753e257bdadb11a298ee455259df21865]
+        # Private repo for publishing certificates.
+        PUBLISHING_MATCH_CERTIFICATE_REPO: ENCRYPTED[3c0e78877d933fc80107aa6f3790fd1cf927250b852d6cb53202be696b4903ed8ca839b809626aaf18050bf7e436fab7]
+        PUBLISHING_MATCH_REPO_TOKEN: ENCRYPTED[3d1230b744c6ed6c788a91bec741b769401dbcd426b18f9af8080bfeefdfc21913ca4047980c5b5b7ce823f12e7b6b19]
+        # Apple Certificates Match Passphrase
+        MATCH_PASSWORD: ENCRYPTED[db07f252234397090e3ec59152d9ec1831f5ecd0ef97d247b1dca757bbb9ef9b7c832a39bce2caf1949ccdf097e59a73]
+      script:
+        - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976
+        - ./dev/bots/deploy_gallery.sh
+
 docker_builder:
   # Only build a new docker image when we tag a release (for dev, beta, or release.)
   only_if: $CIRRUS_TAG != ''
-  env:
+  environment:
     GCLOUD_CREDENTIALS: ENCRYPTED[f7c098d4dd7f5ee1bfee0bb7e944cce72efbe10e97ad6440ae72de4de6a1c24d23f421a2619c668e94377fb64b0bb3e6]
   depends_on:
-    - docs
-    - analyze
-    - tests_widgets-linux
-    - tests_framework_other-linux
-    - tests_extras-linux
-    - tool_tests_general-linux
-    - tool_tests_commands-linux
-    - tool_tests_integration-linux
+    - docs-linux
+    - analyze-linux
+    - framework_tests-widgets-linux
+    - framework_tests-libraries-linux
+    - framework_tests-misc-linux
+    - tool_tests-general-linux
+    - tool_tests-commands-linux
+    - tool_tests-integration-linux
     - build_tests-linux
-    - integration_tests-linux
-    - gradle_tests-linux-shard-1
-    - gradle_tests-linux-shard-2
-    - gradle_embedding_v2_tests-linux-shard-1
-    - gradle_embedding_v2_tests-linux-shard-2
-    - gradle_tests-windows-shard-1
-    - gradle_tests-windows-shard-2
-    - gradle_embedding_v2_tests-windows-shard-1
-    - gradle_embedding_v2_tests-windows-shard-2
-    - release_smoke_tests
-  build_script: "$CIRRUS_WORKING_DIR/dev/ci/docker_linux/docker_build.sh"
-  login_script: "$CIRRUS_WORKING_DIR/dev/ci/docker_linux/docker_login.sh"
-  push_script: "$CIRRUS_WORKING_DIR/dev/ci/docker_linux/docker_push.sh"
+    - hostonly_devicelab_tests-0-linux
+    - hostonly_devicelab_tests-1-linux
+    - hostonly_devicelab_tests-2-linux
+    - hostonly_devicelab_tests-3-linux
+    - hostonly_devicelab_tests-4-linux
+    - hostonly_devicelab_tests-5_last-linux
+    - firebase_test_lab_tests-linux
+  script:
+    - "$CIRRUS_WORKING_DIR/dev/ci/docker_linux/docker_build.sh"
+    - "$CIRRUS_WORKING_DIR/dev/ci/docker_linux/docker_login.sh"
+    - "$CIRRUS_WORKING_DIR/dev/ci/docker_linux/docker_push.sh"
diff --git a/dev/README.md b/dev/README.md
index afe8f26..3c287dd 100644
--- a/dev/README.md
+++ b/dev/README.md
@@ -2,3 +2,6 @@
 during development of the framework. The tools in this directory
 should not be necessary for developing Flutter applications, though of
 course they may be interesting if you are curious.
+
+The tests in this directory are run in the `framework_tests_misc-*`
+shards.
diff --git a/dev/bots/accept_android_sdk_licenses.sh b/dev/bots/accept_android_sdk_licenses.sh
new file mode 100755
index 0000000..a5b6abb
--- /dev/null
+++ b/dev/bots/accept_android_sdk_licenses.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+set -e
+
+# This script is only meant to be run by the Cirrus CI system, not locally.
+# It must be run from the root of the Flutter repo.
+
+function error() {
+  echo "$@" 1>&2
+}
+
+function accept_android_licenses() {
+  yes "y" | flutter doctor --android-licenses > /dev/null 2>&1
+}
+
+echo "Flutter SDK directory is: $PWD"
+
+# Accept licenses.
+echo "Accepting Android licenses."
+accept_android_licenses || (error "Accepting Android licenses failed." && false)
diff --git a/dev/bots/cirrus_setup.sh b/dev/bots/cirrus_setup.sh
deleted file mode 100755
index 451bcbc..0000000
--- a/dev/bots/cirrus_setup.sh
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/bin/bash
-set -e
-
-function error() {
-  echo "$@" 1>&2
-}
-
-# This script is only meant to be run by the Cirrus CI system, not locally.
-# It must be run from the root of the Flutter repo.
-
-function accept_android_licenses() {
-  yes "y" | flutter doctor --android-licenses > /dev/null 2>&1
-}
-
-echo "Flutter SDK directory is: $PWD"
-
-# Run flutter to download dependencies and precompile things, and to disable
-# analytics on the bots.
-echo "Downloading build dependencies and pre-compiling Flutter snapshot"
-./bin/flutter config --no-analytics
-
-# Run doctor, to print it to the log for debugging purposes.
-./bin/flutter doctor -v
-
-# Accept licenses.
-echo "Accepting Android licenses."
-accept_android_licenses || (error "Accepting Android licenses failed." && false)
-
-# Run pub get in all the repo packages.
-echo "Updating packages for Flutter."
-./bin/flutter update-packages
diff --git a/dev/bots/deploy_gallery.sh b/dev/bots/deploy_gallery.sh
index f372fec..86d34d9 100755
--- a/dev/bots/deploy_gallery.sh
+++ b/dev/bots/deploy_gallery.sh
@@ -24,88 +24,90 @@
 
 cd "$FLUTTER_ROOT"
 
-if [[ "$SHARD" = "deploy_gallery" ]]; then
-  version="$(<version)"
-  if [[ "$OS" == "linux" ]]; then
-    echo "Building Flutter Gallery $version for Android..."
-
-    # ANDROID_SDK_ROOT must be set in the env.
+version="$(<version)"
+if [[ "$OS" == "linux" ]]; then
+  echo "Building Flutter Gallery $version for Android..."
+  # ANDROID_SDK_ROOT must be set in the env.
+  (
+    cd examples/flutter_gallery
+    flutter build apk --release -t lib/main_publish.dart
+  )
+  echo "Android Flutter Gallery built"
+  if [[ -z "$CIRRUS_PR" && "$CIRRUS_BRANCH" == "dev" && "$version" != *"pre"* ]]; then
+    echo "Deploying Flutter Gallery $version to Play Store..."
+    set +x # Don't echo back the below.
+    if [ -n "$ANDROID_GALLERY_UPLOAD_KEY" ]; then
+      echo "$ANDROID_GALLERY_UPLOAD_KEY" | base64 --decode > /root/.android/debug.keystore
+    fi
+    set -x
     (
-      cd examples/flutter_gallery
-      flutter build apk --release -t lib/main_publish.dart
+      cd examples/flutter_gallery/android
+      fastlane deploy_play_store
     )
-    echo "Android Flutter Gallery built"
-    if [[ -z "$CIRRUS_PR" && "$CIRRUS_BRANCH" == "dev" && "$version" != *"pre"* ]]; then
-      echo "Deploying Flutter Gallery $version to Play Store..."
-      set +x # Don't echo back the below.
-      if [ -n "$ANDROID_GALLERY_UPLOAD_KEY" ]; then
-        echo "$ANDROID_GALLERY_UPLOAD_KEY" | base64 --decode > /root/.android/debug.keystore
-      fi
-      set -x
+  else
+    echo "(Not deploying; Flutter Gallery is only deployed to Play store for tagged dev branch commits.)"
+  fi
+elif [[ "$OS" == "darwin" ]]; then
+  echo "Building Flutter Gallery $version for iOS..."
+  (
+    cd examples/flutter_gallery
+    flutter build ios --release --no-codesign -t lib/main_publish.dart
+
+    # flutter build ios will run CocoaPods script. Check generated locations.
+    if [[ ! -d "ios/Pods" ]]; then
+      echo "Error: pod install failed to setup plugins"
+      exit 1
+    fi
+
+    if [[ ! -d "ios/.symlinks/plugins" ]]; then
+      echo "Error: pod install failed to setup plugin symlinks"
+      exit 1
+    fi
+
+    if [[ -d "ios/.symlinks/flutter" ]]; then
+      echo "Error: pod install created flutter symlink"
+      exit 1
+    fi
+
+    if [[ ! -d "build/ios/iphoneos/Runner.app/Frameworks/App.framework/flutter_assets" ]]; then
+      echo "Error: flutter_assets not assembled"
+      exit 1
+    fi
+
+    if [[
+      -d "build/ios/iphoneos/Runner.app/Frameworks/App.framework/flutter_assets/isolate_snapshot_data" ||
+      -d "build/ios/iphoneos/Runner.app/Frameworks/App.framework/flutter_assets/kernel_blob.bin" ||
+      -d "build/ios/iphoneos/Runner.app/Frameworks/App.framework/flutter_assets/vm_snapshot_data"
+     ]]; then
+      echo "Error: compiled debug version of app with --release flag"
+      exit 1
+    fi
+  )
+  echo "iOS Flutter Gallery built"
+  if [[ -z "$CIRRUS_PR" ]]; then
+    if [[ "$CIRRUS_BRANCH" == "dev" && "$version" != *"pre"* ]]; then
+      echo "Archiving with distribution profile and deploying to TestFlight..."
       (
-        cd examples/flutter_gallery/android
-        fastlane deploy_play_store
+        cd examples/flutter_gallery/ios
+        export DELIVER_ITMSTRANSPORTER_ADDITIONAL_UPLOAD_PARAMETERS="-t DAV"
+        fastlane build_and_deploy_testflight upload:true
       )
     else
-      echo "Not deployed: Flutter Gallery is only deployed to the Play Store on merged and tagged dev branch commits"
+      # On iOS the signing can break as well, so we verify this regularly (not just
+      # on tagged dev branch commits). We can only do this post-merge, though, because
+      # the secrets aren't available on PRs.
+      echo "Testing archiving with distribution profile..."
+      (
+        cd examples/flutter_gallery/ios
+        fastlane build_and_deploy_testflight
+      )
+      echo "(Not deploying; Flutter Gallery is only deployed to TestFlight for tagged dev branch commits.)"
     fi
-  elif [[ "$OS" == "darwin" ]]; then
-    echo "Building Flutter Gallery $version for iOS..."
-    (
-      cd examples/flutter_gallery
-      flutter build ios --release --no-codesign -t lib/main_publish.dart
-
-      # flutter build ios will run CocoaPods script. Check generated locations.
-      if [[ ! -d "ios/Pods" ]]; then
-        echo "Error: pod install failed to setup plugins"
-        exit 1
-      fi
-
-      if [[ ! -d "ios/.symlinks/plugins" ]]; then
-        echo "Error: pod install failed to setup plugin symlinks"
-        exit 1
-      fi
-
-      if [[ -d "ios/.symlinks/flutter" ]]; then
-        echo "Error: pod install created flutter symlink"
-        exit 1
-      fi
-
-      if [[ ! -d "build/ios/iphoneos/Runner.app/Frameworks/App.framework/flutter_assets" ]]; then
-        echo "Error: flutter_assets not assembled"
-        exit 1
-      fi
-
-      if [[ 
-        -d "build/ios/iphoneos/Runner.app/Frameworks/App.framework/flutter_assets/isolate_snapshot_data" ||
-        -d "build/ios/iphoneos/Runner.app/Frameworks/App.framework/flutter_assets/kernel_blob.bin" ||
-        -d "build/ios/iphoneos/Runner.app/Frameworks/App.framework/flutter_assets/vm_snapshot_data"
-       ]]; then
-        echo "Error: compiled debug version of app with --release flag"
-        exit 1
-      fi
-    )
-    echo "iOS Flutter Gallery built"
-    if [[ -z "$CIRRUS_PR" ]]; then
-      if [[ "$CIRRUS_BRANCH" == "dev" && "$version" != *"pre"* ]]; then
-        echo "Archiving with distribution profile and deploying to TestFlight..."
-        (
-          cd examples/flutter_gallery/ios
-          export DELIVER_ITMSTRANSPORTER_ADDITIONAL_UPLOAD_PARAMETERS="-t DAV"
-          fastlane build_and_deploy_testflight upload:true
-        )
-      else
-        echo "Archiving with distribution profile..."
-        (
-          cd examples/flutter_gallery/ios
-          fastlane build_and_deploy_testflight
-        )
-        echo "Archive is only deployed to TestFlight on tagged dev branch commits"
-      fi
-    else
-      echo "Not deployed: Flutter Gallery is only deployed to TestFlight on merged and tagged dev branch commits"
-    fi
+  else
+    echo "(Not archiving or deploying; Flutter Gallery archiving is only tested post-commit.)"
   fi
 else
-  echo "Doing nothing: not on the 'deploy_gallery' SHARD."
+  echo "Unknown OS: $OS"
+  echo "Aborted."
+  exit 1
 fi
diff --git a/dev/bots/run_command.dart b/dev/bots/run_command.dart
index 634a232..f240a5c 100644
--- a/dev/bots/run_command.dart
+++ b/dev/bots/run_command.dart
@@ -95,7 +95,6 @@
   OutputMode outputMode = OutputMode.print,
   CapturedOutput output,
   bool skip = false,
-  bool expectFlaky = false,
   bool Function(String) removeLine,
 }) async {
   assert((outputMode == OutputMode.capture) == (output != null),
@@ -145,10 +144,6 @@
     output.stderr = _flattenToString(await savedStderr);
   }
 
-  // If the test is flaky we don't care about the actual exit.
-  if (expectFlaky)
-    return;
-
   if ((exitCode == 0) == expectNonZeroExit || (expectedExitCode != null && exitCode != expectedExitCode)) {
     if (failureMessage != null) {
       print(failureMessage);
diff --git a/dev/bots/test.dart b/dev/bots/test.dart
index 9914dd2..fea54fb 100644
--- a/dev/bots/test.dart
+++ b/dev/bots/test.dart
@@ -4,11 +4,11 @@
 
 import 'dart:async';
 import 'dart:io';
+import 'dart:math' as math;
 
 import 'package:googleapis/bigquery/v2.dart' as bq;
 import 'package:googleapis_auth/auth_io.dart' as auth;
 import 'package:http/http.dart' as http;
-import 'package:meta/meta.dart';
 import 'package:path/path.dart' as path;
 
 import 'flutter_compact_formatter.dart';
@@ -30,21 +30,54 @@
 final String pub = path.join(flutterRoot, 'bin', 'cache', 'dart-sdk', 'bin', Platform.isWindows ? 'pub.bat' : 'pub');
 final String pubCache = path.join(flutterRoot, '.pub-cache');
 final String toolRoot = path.join(flutterRoot, 'packages', 'flutter_tools');
+
+/// The arguments to pass to `flutter test` (typically the local engine
+/// configuration) -- prefilled with the arguments passed to test.dart.
 final List<String> flutterTestArgs = <String>[];
 
 final bool useFlutterTestFormatter = Platform.environment['FLUTTER_TEST_FORMATTER'] == 'true';
 final bool canUseBuildRunner = Platform.environment['FLUTTER_TEST_NO_BUILD_RUNNER'] != 'true';
 
-const Map<String, ShardRunner> _kShards = <String, ShardRunner>{
-  'tests': _runTests,
-  'web_tests': _runWebTests,
-  'tool_tests': _runToolTests,
-  'tool_coverage': _runToolCoverage,
-  'build_tests': _runBuildTests,
-  'coverage': _runCoverage,
-  'integration_tests': _runIntegrationTests,
-  'add2app_test': _runAdd2AppTest,
-};
+/// The number of Cirrus jobs that run host-only devicelab tests in parallel.
+///
+/// WARNING: if you change this number, also change .cirrus.yml
+/// and make sure it runs _all_ shards.
+const int kDeviceLabShardCount = 6;
+
+/// The number of Cirrus jobs that run Web tests in parallel.
+///
+/// WARNING: if you change this number, also change .cirrus.yml
+/// and make sure it runs _all_ shards.
+///
+/// The last shard also runs the Web plugin tests.
+const int kWebShardCount = 6;
+
+/// Maximum number of Web tests to run in a single `flutter test`. We found that
+/// large batches can get flaky, possibly because we reuse a single instance of
+/// the browser, and after many tests the browser's state gets corrupted.
+const int kWebBatchSize = 20;
+
+/// Tests that we don't run on Web for various reasons.
+//
+// TODO(yjbanov): we're getting rid of these blacklists as part of https://github.com/flutter/flutter/projects/60
+const List<String> kWebTestDirectoryBlacklist = <String>[
+  'cupertino',
+  'examples',
+  'material',
+];
+const List<String> kWebTestFileBlacklist = <String>[
+  'test/widgets/heroes_test.dart',
+  'test/widgets/text_test.dart',
+  'test/widgets/selectable_text_test.dart',
+  'test/widgets/color_filter_test.dart',
+  'test/widgets/editable_text_cursor_test.dart',
+  'test/widgets/shadow_test.dart',
+  'test/widgets/raw_keyboard_listener_test.dart',
+  'test/widgets/editable_text_test.dart',
+  'test/widgets/widget_inspector_test.dart',
+  'test/widgets/draggable_test.dart',
+  'test/widgets/shortcuts_test.dart',
+];
 
 /// When you call this, you can pass additional arguments to pass custom
 /// arguments to flutter test. For example, you might want to call this
@@ -53,31 +86,30 @@
 ///
 /// To run the tool_tests part, run it with SHARD=tool_tests
 ///
-/// For example:
+/// Examples:
 /// SHARD=tool_tests bin/cache/dart-sdk/bin/dart dev/bots/test.dart
 /// bin/cache/dart-sdk/bin/dart dev/bots/test.dart --local-engine=host_debug_unopt
 Future<void> main(List<String> args) async {
   flutterTestArgs.addAll(args);
-
-  final String shard = Platform.environment['SHARD'];
-  if (shard != null) {
-    if (!_kShards.containsKey(shard)) {
-      print('Invalid shard: $shard');
-      print('The available shards are: ${_kShards.keys.join(", ")}');
-      exit(1);
-    }
-    print('${bold}SHARD=$shard$reset');
-    await _kShards[shard]();
-  } else {
-    for (String currentShard in _kShards.keys) {
-      print('${bold}SHARD=$currentShard$reset');
-      await _kShards[currentShard]();
-      print('');
-    }
-  }
+  if (Platform.environment.containsKey(CIRRUS_TASK_NAME))
+    print('Running task: ${Platform.environment[CIRRUS_TASK_NAME]}');
+  print('═' * 80);
+  await _runSmokeTests();
+  print('═' * 80);
+  await selectShard(const <String, ShardRunner>{
+    'add_to_app_tests': _runAddToAppTests,
+    'build_tests': _runBuildTests,
+    'framework_coverage': _runFrameworkCoverage,
+    'framework_tests': _runFrameworkTests,
+    'hostonly_devicelab_tests': _runHostOnlyDeviceLabTests,
+    'tool_coverage': _runToolCoverage,
+    'tool_tests': _runToolTests,
+    'web_tests': _runWebTests,
+  });
 }
 
 Future<void> _runSmokeTests() async {
+  print('${green}Running smoketests...$reset');
   // Verify that the tests actually return failure on failure and success on
   // success.
   final String automatedTests = path.join(flutterRoot, 'dev', 'automated_tests');
@@ -108,10 +140,11 @@
     script: path.join('test_smoke_test', 'pending_timer_fail_test.dart'),
     expectFailure: true,
     printOutput: false,
-    outputChecker: (CapturedOutput output) =>
-      output.stdout.contains('failingPendingTimerTest')
-      ? null
-      : 'Failed to find the stack trace for the pending Timer.',
+    outputChecker: (CapturedOutput output) {
+      return output.stdout.contains('failingPendingTimerTest')
+        ? null
+        : 'Failed to find the stack trace for the pending Timer.';
+    }
   );
   // We run the remaining smoketests in parallel, because they each take some
   // time to run (e.g. compiling), so we don't want to run them in series,
@@ -153,8 +186,11 @@
   );
 
   // Verify that we correctly generated the version file.
-  final bool validVersion = await verifyVersion(path.join(flutterRoot, 'version'));
-  if (!validVersion) {
+  final String versionError = await verifyVersion(File(path.join(flutterRoot, 'version')));
+  if (versionError != null) {
+    print(redLine);
+    print(versionError);
+    print(redLine);
     exit(1);
   }
 }
@@ -179,7 +215,7 @@
     final http.Client client = await auth.clientViaServiceAccount(accountCredentials, scopes);
     return bq.BigqueryApi(client);
   } catch (e) {
-    print('Failed to get BigQuery API client.');
+    print('${red}Failed to get BigQuery API client.$reset');
     print(e);
     return null;
   }
@@ -203,7 +239,6 @@
 
 Future<void> _runToolTests() async {
   final bq.BigqueryApi bigqueryApi = await _getBigqueryApi();
-  await _runSmokeTests();
 
   const String kDotShard = '.shard';
   const String kTest = 'test';
@@ -232,11 +267,10 @@
   await selectSubshard(subshards);
 }
 
-/// Verifies that AOT, APK, and IPA (if on macOS) builds the
-/// examples apps without crashing. It does not actually
-/// launch the apps. That happens later in the devicelab. This is
-/// just a smoke-test. In particular, this will verify we can build
-/// when there are spaces in the path name for the Flutter SDK and
+/// Verifies that AOT, APK, and IPA (if on macOS) builds the examples apps
+/// without crashing. It does not actually launch the apps. That happens later
+/// in the devicelab. This is just a smoke-test. In particular, this will verify
+/// we can build when there are spaces in the path name for the Flutter SDK and
 /// target app.
 Future<void> _runBuildTests() async {
   final Stream<FileSystemEntity> exampleDirectories = Directory(path.join(flutterRoot, 'examples')).list();
@@ -245,10 +279,11 @@
       continue;
     }
     final String examplePath = fileEntity.path;
-
     await _flutterBuildAot(examplePath);
     await _flutterBuildApk(examplePath);
-    await _flutterBuildIpa(examplePath);
+    if (Platform.isMacOS) {
+      await _flutterBuildIpa(examplePath);
+    }
   }
   // Web compilation tests.
   await _flutterBuildDart2js(path.join('dev', 'integration_tests', 'web'), path.join('lib', 'main.dart'));
@@ -256,12 +291,44 @@
   await _flutterBuildDart2js(path.join('dev', 'integration_tests', 'web_compile_tests'),
     path.join('lib', 'dart_io_import.dart'),
   );
+}
 
-  print('${bold}DONE: All build tests successful.$reset');
+Future<void> _flutterBuildAot(String relativePathToApplication) async {
+  print('${green}Testing AOT build$reset for $cyan$relativePathToApplication$reset...');
+  await runCommand(flutter,
+    <String>['build', 'aot', '-v'],
+    workingDirectory: path.join(flutterRoot, relativePathToApplication),
+  );
+}
+
+Future<void> _flutterBuildApk(String relativePathToApplication) async {
+  print('${green}Testing APK --debug build$reset for $cyan$relativePathToApplication$reset...');
+  await runCommand(flutter,
+    <String>['build', 'apk', '--debug', '-v'],
+    workingDirectory: path.join(flutterRoot, relativePathToApplication),
+  );
+}
+
+Future<void> _flutterBuildIpa(String relativePathToApplication) async {
+  assert(Platform.isMacOS);
+  print('${green}Testing IPA build$reset for $cyan$relativePathToApplication$reset...');
+  // Install Cocoapods.  We don't have these checked in for the examples,
+  // and build ios doesn't take care of it automatically.
+  final File podfile = File(path.join(flutterRoot, relativePathToApplication, 'ios', 'Podfile'));
+  if (podfile.existsSync()) {
+    await runCommand('pod',
+      <String>['install'],
+      workingDirectory: podfile.parent.path,
+    );
+  }
+  await runCommand(flutter,
+    <String>['build', 'ios', '--no-codesign', '--debug', '-v'],
+    workingDirectory: path.join(flutterRoot, relativePathToApplication),
+  );
 }
 
 Future<void> _flutterBuildDart2js(String relativePathToApplication, String target, { bool expectNonZeroExit = false }) async {
-  print('Running Dart2JS build tests...');
+  print('${green}Testing Dart2JS build$reset for $cyan$relativePathToApplication$reset...');
   await runCommand(flutter,
     <String>['build', 'web', '-v', '--target=$target'],
     workingDirectory: path.join(flutterRoot, relativePathToApplication),
@@ -270,211 +337,101 @@
       'FLUTTER_WEB': 'true',
     },
   );
-  print('Done.');
 }
 
-Future<void> _flutterBuildAot(String relativePathToApplication) async {
-  print('Running AOT build tests...');
-  await runCommand(flutter,
-    <String>['build', 'aot', '-v'],
-    workingDirectory: path.join(flutterRoot, relativePathToApplication),
-    expectNonZeroExit: false,
-  );
-  print('Done.');
-}
-
-Future<void> _flutterBuildApk(String relativePathToApplication) async {
-  if (
-        (Platform.environment['ANDROID_HOME']?.isEmpty ?? true) &&
-        (Platform.environment['ANDROID_SDK_ROOT']?.isEmpty ?? true)) {
-    return;
-  }
-  print('Running APK build tests...');
-  await runCommand(flutter,
-    <String>['build', 'apk', '--debug', '-v'],
-    workingDirectory: path.join(flutterRoot, relativePathToApplication),
-    expectNonZeroExit: false,
-  );
-  print('Done.');
-}
-
-Future<void> _flutterBuildIpa(String relativePathToApplication) async {
-  if (!Platform.isMacOS) {
-    return;
-  }
-  print('Running IPA build tests...');
-  // Install Cocoapods.  We don't have these checked in for the examples,
-  // and build ios doesn't take care of it automatically.
-  final File podfile = File(path.join(flutterRoot, relativePathToApplication, 'ios', 'Podfile'));
-  if (podfile.existsSync()) {
-    await runCommand('pod',
-      <String>['install'],
-      workingDirectory: podfile.parent.path,
-      expectNonZeroExit: false,
+Future<void> _runAddToAppTests() async {
+  if (Platform.isMacOS) {
+    print('${green}Running add-to-app iOS integration tests$reset...');
+    final String addToAppDir = path.join(flutterRoot, 'dev', 'integration_tests', 'ios_add2app');
+    await runCommand('./build_and_test.sh',
+      <String>[],
+      workingDirectory: addToAppDir,
     );
   }
-  await runCommand(flutter,
-    <String>['build', 'ios', '--no-codesign', '--debug', '-v'],
-    workingDirectory: path.join(flutterRoot, relativePathToApplication),
-    expectNonZeroExit: false,
-  );
-  print('Done.');
 }
 
-Future<void> _runAdd2AppTest() async {
-  if (!Platform.isMacOS) {
-    return;
-  }
-  print('Running Add2App iOS integration tests...');
-  final String add2AppDir = path.join(flutterRoot, 'dev', 'integration_tests', 'ios_add2app');
-  await runCommand('./build_and_test.sh',
-    <String>[],
-    workingDirectory: add2AppDir,
-    expectNonZeroExit: false,
-  );
-  print('Done.');
-}
-
-Future<void> _runTests() async {
+Future<void> _runFrameworkTests() async {
   final bq.BigqueryApi bigqueryApi = await _getBigqueryApi();
-  await _runSmokeTests();
-  final String subShard = Platform.environment['SUBSHARD'];
 
   Future<void> runWidgets() async {
-    await _runFlutterTest(
-      path.join(flutterRoot, 'packages', 'flutter'),
-      tableData: bigqueryApi?.tabledata,
-      tests: <String>[
-        path.join('test', 'widgets') + path.separator,
-      ],
-    );
-    // Only packages/flutter/test/widgets/widget_inspector_test.dart really
-    // needs to be run with --track-widget-creation but it is nice to run
-    // all of the tests in package:flutter with the flag to ensure that
-    // the Dart kernel transformer triggered by the flag does not break anything.
+    print('${green}Running packages/flutter tests for$reset: ${cyan}test/widgets/$reset');
     await _runFlutterTest(
       path.join(flutterRoot, 'packages', 'flutter'),
       options: <String>['--track-widget-creation'],
       tableData: bigqueryApi?.tabledata,
-      tests: <String>[
-        path.join('test', 'widgets') + path.separator,
-      ],
+      tests: <String>[ path.join('test', 'widgets') + path.separator ],
     );
+    await _runFlutterTest(
+      path.join(flutterRoot, 'packages', 'flutter'),
+      options: <String>['--no-track-widget-creation'],
+      tableData: bigqueryApi?.tabledata,
+      tests: <String>[ path.join('test', 'widgets') + path.separator ],
+    );
+    // Try compiling code outside of the packages/flutter directory with and without --track-widget-creation
+    await _runFlutterTest(path.join(flutterRoot, 'examples', 'flutter_gallery'), options: <String>['--track-widget-creation'], tableData: bigqueryApi?.tabledata);
+    await _runFlutterTest(path.join(flutterRoot, 'examples', 'flutter_gallery'), options: <String>['--no-track-widget-creation'], tableData: bigqueryApi?.tabledata);
   }
 
-  Future<void> runFrameworkOthers() async {
+  Future<void> runLibraries() async {
     final List<String> tests = Directory(path.join(flutterRoot, 'packages', 'flutter', 'test'))
       .listSync(followLinks: false, recursive: false)
       .whereType<Directory>()
       .where((Directory dir) => dir.path.endsWith('widgets') == false)
-      .map((Directory dir) => path.join('test', path.basename(dir.path)) + path.separator)
+      .map<String>((Directory dir) => path.join('test', path.basename(dir.path)) + path.separator)
       .toList();
-
-    print('Running tests for: ${tests.join(';')}');
-
-    await _runFlutterTest(
-      path.join(flutterRoot, 'packages', 'flutter'),
-      tableData: bigqueryApi?.tabledata,
-      tests: tests,
-    );
-    // Only packages/flutter/test/widgets/widget_inspector_test.dart really
-    // needs to be run with --track-widget-creation but it is nice to run
-    // all of the tests in package:flutter with the flag to ensure that
-    // the Dart kernel transformer triggered by the flag does not break anything.
+    print('${green}Running packages/flutter tests$reset for: $cyan${tests.join(", ")}$reset');
     await _runFlutterTest(
       path.join(flutterRoot, 'packages', 'flutter'),
       options: <String>['--track-widget-creation'],
       tableData: bigqueryApi?.tabledata,
       tests: tests,
     );
+    await _runFlutterTest(
+      path.join(flutterRoot, 'packages', 'flutter'),
+      options: <String>['--no-track-widget-creation'],
+      tableData: bigqueryApi?.tabledata,
+      tests: tests,
+    );
   }
 
-  Future<void> runExtras() async {
-    await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_localizations'), tableData: bigqueryApi?.tabledata);
-    await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_driver'), tableData: bigqueryApi?.tabledata);
-    await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_test'), tableData: bigqueryApi?.tabledata);
-    await _runFlutterTest(path.join(flutterRoot, 'packages', 'fuchsia_remote_debug_protocol'), tableData: bigqueryApi?.tabledata);
+  Future<void> runMisc() async {
+    print('${green}Running package tests$reset for directories other than packages/flutter');
     await _pubRunTest(path.join(flutterRoot, 'dev', 'bots'), tableData: bigqueryApi?.tabledata);
     await _pubRunTest(path.join(flutterRoot, 'dev', 'devicelab'), tableData: bigqueryApi?.tabledata);
     await _pubRunTest(path.join(flutterRoot, 'dev', 'snippets'), tableData: bigqueryApi?.tabledata);
     await _runFlutterTest(path.join(flutterRoot, 'dev', 'integration_tests', 'android_semantics_testing'), tableData: bigqueryApi?.tabledata);
     await _runFlutterTest(path.join(flutterRoot, 'dev', 'manual_tests'), tableData: bigqueryApi?.tabledata);
     await _runFlutterTest(path.join(flutterRoot, 'dev', 'tools', 'vitool'), tableData: bigqueryApi?.tabledata);
+    await _runFlutterTest(path.join(flutterRoot, 'examples', 'catalog'), tableData: bigqueryApi?.tabledata);
     await _runFlutterTest(path.join(flutterRoot, 'examples', 'hello_world'), tableData: bigqueryApi?.tabledata);
     await _runFlutterTest(path.join(flutterRoot, 'examples', 'layers'), tableData: bigqueryApi?.tabledata);
     await _runFlutterTest(path.join(flutterRoot, 'examples', 'stocks'), tableData: bigqueryApi?.tabledata);
-    await _runFlutterTest(path.join(flutterRoot, 'examples', 'flutter_gallery'), tableData: bigqueryApi?.tabledata);
-    // Regression test to ensure that code outside of package:flutter can run
-    // with --track-widget-creation.
-    await _runFlutterTest(path.join(flutterRoot, 'examples', 'flutter_gallery'), options: <String>['--track-widget-creation'], tableData: bigqueryApi?.tabledata);
-    await _runFlutterTest(path.join(flutterRoot, 'examples', 'catalog'), tableData: bigqueryApi?.tabledata);
-    // Smoke test for code generation.
-    await _runFlutterTest(path.join(flutterRoot, 'dev', 'integration_tests', 'codegen'), tableData: bigqueryApi?.tabledata, environment: <String, String>{
-      'FLUTTER_EXPERIMENTAL_BUILD': 'true',
-    });
-  }
-  switch (subShard) {
-    case 'widgets':
-      await runWidgets();
-      break;
-    case 'framework_other':
-      await runFrameworkOthers();
-      break;
-    case 'extras':
-      runExtras();
-      break;
-    default:
-      print('Unknown sub-shard $subShard, running all tests!');
-      await runWidgets();
-      await runFrameworkOthers();
-      await runExtras();
-
+    await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_driver'), tableData: bigqueryApi?.tabledata);
+    await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_localizations'), tableData: bigqueryApi?.tabledata);
+    await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_test'), tableData: bigqueryApi?.tabledata);
+    await _runFlutterTest(path.join(flutterRoot, 'packages', 'fuchsia_remote_debug_protocol'), tableData: bigqueryApi?.tabledata);
+    await _runFlutterTest(
+      path.join(flutterRoot, 'dev', 'integration_tests', 'codegen'),
+      tableData: bigqueryApi?.tabledata,
+      environment: <String, String>{
+        'FLUTTER_EXPERIMENTAL_BUILD': 'true',
+      },
+    );
   }
 
-  print('${bold}DONE: All tests successful.$reset');
+  await selectSubshard(<String, ShardRunner>{
+    'widgets': runWidgets,
+    'libraries': runLibraries,
+    'misc': runMisc,
+  });
 }
 
-// TODO(yjbanov): we're getting rid of these blacklists as part of https://github.com/flutter/flutter/projects/60
-const List<String> kWebTestDirectoryBlacklist = <String>[
-  'test/cupertino',
-  'test/examples',
-  'test/material',
-];
-const List<String> kWebTestFileBlacklist = <String>[
-  'test/widgets/heroes_test.dart',
-  'test/widgets/text_test.dart',
-  'test/widgets/selectable_text_test.dart',
-  'test/widgets/color_filter_test.dart',
-  'test/widgets/editable_text_cursor_test.dart',
-  'test/widgets/shadow_test.dart',
-  'test/widgets/raw_keyboard_listener_test.dart',
-  'test/widgets/editable_text_test.dart',
-  'test/widgets/widget_inspector_test.dart',
-  'test/widgets/draggable_test.dart',
-  'test/widgets/shortcuts_test.dart',
-];
-
-Future<void> _runWebTests() async {
-  final Directory flutterPackageDir = Directory(path.join(flutterRoot, 'packages', 'flutter'));
-  final Directory testDir = Directory(path.join(flutterPackageDir.path, 'test'));
-
-  final List<String> directories = testDir
-    .listSync()
-    .whereType<Directory>()
-    .map<String>((Directory dir) => path.relative(dir.path, from: flutterPackageDir.path))
-    .where((String relativePath) => !kWebTestDirectoryBlacklist.contains(relativePath))
-    .toList();
-
-  await _runFlutterWebTest(flutterPackageDir.path, tests: directories);
-  await _runFlutterWebTest(path.join(flutterRoot, 'packages', 'flutter_web_plugins'), tests: <String>['test']);
-}
-
-Future<void> _runCoverage() async {
+Future<void> _runFrameworkCoverage() async {
   final File coverageFile = File(path.join(flutterRoot, 'packages', 'flutter', 'coverage', 'lcov.info'));
   if (!coverageFile.existsSync()) {
     print('${red}Coverage file not found.$reset');
-    print('Expected to find: ${coverageFile.absolute}');
-    print('This file is normally obtained by running `flutter update-packages`.');
+    print('Expected to find: $cyan${coverageFile.absolute}$reset');
+    print('This file is normally obtained by running `${green}flutter update-packages$reset`.');
     exit(1);
   }
   coverageFile.deleteSync();
@@ -483,11 +440,98 @@
   );
   if (!coverageFile.existsSync()) {
     print('${red}Coverage file not found.$reset');
-    print('Expected to find: ${coverageFile.absolute}');
-    print('This file should have been generated by the `flutter test --coverage` script, but was not.');
+    print('Expected to find: $cyan${coverageFile.absolute}$reset');
+    print('This file should have been generated by the `${green}flutter test --coverage$reset` script, but was not.');
     exit(1);
   }
-  print('${bold}DONE: Coverage collection successful.$reset');
+}
+
+Future<void> _runWebTests() async {
+  final Map<String, ShardRunner> subshards = <String, ShardRunner>{};
+
+  final Directory flutterPackageDirectory = Directory(path.join(flutterRoot, 'packages', 'flutter'));
+  final Directory flutterPackageTestDirectory = Directory(path.join(flutterPackageDirectory.path, 'test'));
+
+  final List<String> allTests = flutterPackageTestDirectory
+    .listSync()
+    .whereType<Directory>()
+    .where((Directory directory) => !kWebTestDirectoryBlacklist.contains(path.basename(directory.path)))
+    .expand((Directory directory) => directory
+      .listSync(recursive: true)
+      .where((FileSystemEntity entity) => entity.path.endsWith('_test.dart'))
+    )
+    .whereType<File>()
+    .map<String>((File file) => path.relative(file.path, from: flutterPackageDirectory.path))
+    .where((String filePath) => !kWebTestFileBlacklist.contains(filePath))
+    .toList()
+    // Finally we shuffle the list because we want the average cost per file to be uniformly
+    // distributed. If the list is not sorted then different shards and batches may have
+    // very different characteristics.
+    // We use a constant seed for repeatability.
+    ..shuffle(math.Random(0));
+
+  assert(kWebShardCount >= 1);
+  final int testsPerShard = (allTests.length / kWebShardCount).ceil();
+  assert(testsPerShard * kWebShardCount >= allTests.length);
+
+  // This for loop computes all but the last shard.
+  for (int index = 0; index < kWebShardCount - 1; index += 1) {
+    subshards['$index'] = () => _runFlutterWebTest(
+      flutterPackageDirectory.path,
+      allTests.sublist(
+        index * testsPerShard,
+        (index + 1) * testsPerShard,
+      ),
+    );
+  }
+
+  // The last shard also runs the flutter_web_plugins tests.
+  //
+  // We make sure the last shard ends in _last so it's easier to catch mismatches
+  // between `.cirrus.yml` and `test.dart`.
+  subshards['${kWebShardCount - 1}_last'] = () async {
+    await _runFlutterWebTest(
+      flutterPackageDirectory.path,
+      allTests.sublist(
+        (kWebShardCount - 1) * testsPerShard,
+        allTests.length,
+      ),
+    );
+    await _runFlutterWebTest(
+      path.join(flutterRoot, 'packages', 'flutter_web_plugins'),
+      <String>['test'],
+    );
+  };
+
+  await selectSubshard(subshards);
+}
+
+Future<void> _runFlutterWebTest(String workingDirectory, List<String> tests) async {
+  final List<String> batch = <String>[];
+  for (int i = 0; i < tests.length; i += 1) {
+    final String testFilePath = tests[i];
+    batch.add(testFilePath);
+    if (batch.length == kWebBatchSize || i == tests.length - 1) {
+      await runCommand(
+        flutter,
+        <String>[
+          'test',
+          if (ciProvider == CiProviders.cirrus)
+            '--concurrency=1',  // do not parallelize on Cirrus, to reduce flakiness
+          '-v',
+          '--platform=chrome',
+          ...?flutterTestArgs,
+          ...batch,
+        ],
+        workingDirectory: workingDirectory,
+        environment: <String, String>{
+          'FLUTTER_WEB': 'true',
+          'FLUTTER_LOW_RESOURCE_MODE': 'true',
+        },
+      );
+      batch.clear();
+    }
+  }
 }
 
 Future<void> _pubRunTest(String workingDirectory, {
@@ -496,14 +540,33 @@
   bool useBuildRunner = false,
   bq.TabledataResourceApi tableData,
 }) async {
-  final List<String> args = <String>['run', '--verbose'];
+  final List<String> args = <String>['run'];
   if (useBuildRunner) {
-    args.addAll(<String>['build_runner', 'test', '--']);
+    final String posixTestPath = path.posix.joinAll(path.split(testPath));
+    args.addAll(<String>[
+      'build_runner',
+      'test',
+      '--build-filter=$posixTestPath/*.dill',
+      '--build-filter=$posixTestPath/**/*.dill',
+      '--',
+    ]);
   } else {
     args.add('test');
   }
   args.add(useFlutterTestFormatter ? '-rjson' : '-rcompact');
-  args.add('-j1'); // TODO(ianh): Scale based on CPUs.
+  int cpus;
+  final String cpuVariable = Platform.environment['CPU']; // CPU is set in cirrus.yml
+  if (cpuVariable != null) {
+    cpus = int.tryParse(cpuVariable, radix: 10);
+    if (cpus == null) {
+      print('${red}The CPU environment variable, if set, must be set to the integer number of available cores.$reset');
+      print('Actual value: "$cpuVariable"');
+      exit(1);
+    }
+  } else {
+    cpus = 2; // Don't default to 1, otherwise we won't catch race conditions.
+  }
+  args.add('-j$cpus');
   if (!hasColor)
     args.add('--no-color');
   if (testPath != null)
@@ -547,238 +610,6 @@
   }
 }
 
-void deleteFile(String path) {
-  // There's a race condition here but in theory we're not racing anyone
-  // while this script runs, so should be ok.
-  final File file = File(path);
-  if (file.existsSync())
-    file.deleteSync();
-}
-
-enum CiProviders {
-  cirrus,
-  luci,
-}
-
-CiProviders _getCiProvider() {
-  if (Platform.environment['CIRRUS_CI'] == 'true') {
-    return CiProviders.cirrus;
-  }
-  if (Platform.environment['LUCI_CONTEXT'] != null) {
-    return CiProviders.luci;
-  }
-  return null;
-}
-
-String _getCiProviderName() {
-  switch(_getCiProvider()) {
-    case CiProviders.cirrus:
-      return 'cirrusci';
-    case CiProviders.luci:
-      return 'luci';
-  }
-  return 'unknown';
-}
-
-int _getPrNumber() {
-  switch(_getCiProvider()) {
-    case CiProviders.cirrus:
-      return Platform.environment['CIRRUS_PR'] == null
-          ? -1
-          : int.tryParse(Platform.environment['CIRRUS_PR']);
-    case CiProviders.luci:
-      return -1; // LUCI doesn't know about this.
-  }
-  return -1;
-}
-
-Future<String> _getAuthors() async {
-  final String exe = Platform.isWindows ? '.exe' : '';
-  final String author = await runAndGetStdout(
-    'git$exe', <String>['-c', 'log.showSignature=false', 'log', _getGitHash(), '--pretty="%an <%ae>"'],
-    workingDirectory: flutterRoot,
-  ).first;
-  return author;
-}
-
-String _getCiUrl() {
-  switch(_getCiProvider()) {
-    case CiProviders.cirrus:
-      return 'https://cirrus-ci.com/task/${Platform.environment['CIRRUS_TASK_ID']}';
-    case CiProviders.luci:
-      return 'https://ci.chromium.org/p/flutter/g/framework/console'; // TODO(dnfield): can we get a direct link to the actual build?
-  }
-  return '';
-}
-
-String _getGitHash() {
-  switch(_getCiProvider()) {
-    case CiProviders.cirrus:
-      return Platform.environment['CIRRUS_CHANGE_IN_REPO'];
-    case CiProviders.luci:
-      return 'HEAD'; // TODO(dnfield): Set this in the env for LUCI.
-  }
-  return '';
-}
-
-Future<void> _processTestOutput(
-  FlutterCompactFormatter formatter,
-  Stream<String> testOutput,
-  bq.TabledataResourceApi tableData,
-) async {
-  final Timer heartbeat = Timer.periodic(const Duration(seconds: 30), (Timer timer) {
-    print('Processing...');
-  });
-
-  await testOutput.forEach(formatter.processRawOutput);
-  heartbeat.cancel();
-  formatter.finish();
-  if (tableData == null || formatter.tests.isEmpty) {
-    return;
-  }
-  final bq.TableDataInsertAllRequest request = bq.TableDataInsertAllRequest();
-  final String authors = await _getAuthors();
-  request.rows = List<bq.TableDataInsertAllRequestRows>.from(
-    formatter.tests.map<bq.TableDataInsertAllRequestRows>((TestResult result) =>
-      bq.TableDataInsertAllRequestRows.fromJson(<String, dynamic> {
-        'json': <String, dynamic>{
-          'source': <String, dynamic>{
-            'provider': _getCiProviderName(),
-            'url': _getCiUrl(),
-            'platform': <String, dynamic>{
-              'os': Platform.operatingSystem,
-              'version': Platform.operatingSystemVersion,
-            },
-          },
-          'test': <String, dynamic>{
-            'name': result.name,
-            'result': result.status.toString(),
-            'file': result.path,
-            'line': result.line,
-            'column': result.column,
-            'time': result.totalTime,
-          },
-          'git': <String, dynamic>{
-            'author': authors,
-            'pull_request': _getPrNumber(),
-            'commit': _getGitHash(),
-            'organization': 'flutter',
-            'repository': 'flutter',
-          },
-          'error': result.status != TestStatus.failed ? null : <String, dynamic>{
-            'message': result.errorMessage,
-            'stack_trace': result.stackTrace,
-          },
-          'information': result.messages,
-        },
-      }),
-    ),
-    growable: false,
-  );
-  final bq.TableDataInsertAllResponse response = await tableData.insertAll(request, 'flutter-infra', 'tests', 'ci');
-  if (response.insertErrors != null && response.insertErrors.isNotEmpty) {
-    print('${red}BigQuery insert errors:');
-    print(response.toJson());
-    print(reset);
-  }
-}
-
-class EvalResult {
-  EvalResult({
-    this.stdout,
-    this.stderr,
-    this.exitCode = 0,
-  });
-
-  final String stdout;
-  final String stderr;
-  final int exitCode;
-}
-
-/// The number of Cirrus jobs that run web tests in parallel.
-///
-/// WARNING: if you change this number, also change .cirrus.yml
-/// and make sure it runs _all_ shards.
-const int _kWebShardCount = 6;
-
-Future<void> _runFlutterWebTest(String workingDirectory, {
-  List<String> tests,
-}) async {
-  List<String> allTests = <String>[];
-  for (String testDirPath in tests) {
-    final Directory testDir = Directory(path.join(workingDirectory, testDirPath));
-    allTests.addAll(
-      testDir.listSync(recursive: true)
-        .whereType<File>()
-        .where((File file) => file.path.endsWith('_test.dart'))
-        .map<String>((File file) => path.relative(file.path, from: workingDirectory))
-        .where((String filePath) => !kWebTestFileBlacklist.contains(filePath)),
-    );
-  }
-
-  // If a shard is specified only run tests in that shard.
-  final int webShard = int.tryParse(Platform.environment['WEB_SHARD'] ?? 'n/a');
-  if (webShard != null) {
-    if (webShard >= _kWebShardCount) {
-      throw 'WEB_SHARD must be <= _kWebShardCount, but was $webShard';
-    }
-    final List<String> shard = <String>[];
-    for (int i = webShard; i < allTests.length; i += _kWebShardCount) {
-      shard.add(allTests[i]);
-    }
-    allTests = shard;
-  }
-
-  print(allTests.join('\n'));
-  print('${allTests.length} tests total');
-
-  // Maximum number of tests to run in a single `flutter test`. We found that
-  // large batches can get flaky, possibly because we reuse a single instance
-  // of the browser, and after many tests the browser's state gets corrupted.
-  const int kBatchSize = 20;
-  List<String> batch = <String>[];
-  for (int i = 0; i < allTests.length; i += 1) {
-    final String testFilePath = allTests[i];
-    batch.add(testFilePath);
-    if (batch.length == kBatchSize || i == allTests.length - 1) {
-      await _runFlutterWebTestBatch(workingDirectory, batch: batch);
-      batch = <String>[];
-    }
-  }
-}
-
-Future<void> _runFlutterWebTestBatch(String workingDirectory, {
-  List<String> batch,
-}) async {
-  final List<String> args = <String>[
-    'test',
-    if (_getCiProvider() == CiProviders.cirrus)
-      '--concurrency=1',  // do not parallelize on Cirrus to reduce flakiness
-    '-v',
-    '--platform=chrome',
-    ...?flutterTestArgs,
-    ...batch,
-  ];
-
-  // TODO(jonahwilliams): fix relative path issues to make this unecessary.
-  final Directory oldCurrent = Directory.current;
-  Directory.current = Directory(path.join(flutterRoot, 'packages', 'flutter'));
-  try {
-    await runCommand(
-      flutter,
-      args,
-      workingDirectory: workingDirectory,
-      expectFlaky: false,
-      environment: <String, String>{
-        'FLUTTER_WEB': 'true',
-        'FLUTTER_LOW_RESOURCE_MODE': 'true',
-      },
-    );
-  } finally {
-    Directory.current = oldCurrent;
-  }
-}
-
 Future<void> _runFlutterTest(String workingDirectory, {
   String script,
   bool expectFailure = false,
@@ -790,8 +621,7 @@
   Map<String, String> environment,
   List<String> tests = const <String>[],
 }) async {
-  assert(!printOutput || outputChecker == null,
-      'Output either can be printed or checked but not both');
+  assert(!printOutput || outputChecker == null, 'Output either can be printed or checked but not both');
 
   final List<String> args = <String>[
     'test',
@@ -800,16 +630,15 @@
   ];
 
   final bool shouldProcessOutput = useFlutterTestFormatter && !expectFailure && !options.contains('--coverage');
-  if (shouldProcessOutput) {
+  if (shouldProcessOutput)
     args.add('--machine');
-  }
 
   if (script != null) {
     final String fullScriptPath = path.join(workingDirectory, script);
     if (!FileSystemEntity.isFileSync(fullScriptPath)) {
-      print('Could not find test: $fullScriptPath');
-      print('Working directory: $workingDirectory');
-      print('Script: $script');
+      print('${red}Could not find test$reset: $green$fullScriptPath$reset');
+      print('Working directory: $cyan$workingDirectory$reset');
+      print('Script: $green$script$reset');
       if (!printOutput)
         print('This is one of the tests that does not normally print output.');
       if (skip)
@@ -876,110 +705,290 @@
   }
 }
 
-// the optional `file` argument is an override for testing
-@visibleForTesting
-Future<bool> verifyVersion(String filename, [File file]) async {
-  final RegExp pattern = RegExp(r'^\d+\.\d+\.\d+(\+hotfix\.\d+)?(-pre\.\d+)?$');
-  file ??= File(filename);
-  final String version = await file.readAsString();
-  if (!file.existsSync()) {
-    print('$redLine');
-    print('The version logic failed to create the Flutter version file.');
-    print('$redLine');
-    return false;
-  }
-  if (version == '0.0.0-unknown') {
-    print('$redLine');
-    print('The version logic failed to determine the Flutter version.');
-    print('$redLine');
-    return false;
-  }
-  if (!version.contains(pattern)) {
-    print('$redLine');
-    print('The version logic generated an invalid version string: "$version".');
-    print('$redLine');
-    return false;
-  }
-  return true;
-}
-
-Future<void> _runIntegrationTests() async {
-  final String subShard = Platform.environment['SUBSHARD'];
-
-  switch (subShard) {
-    case 'gradle1':
-    case 'gradle2':
-      // This runs some gradle integration tests if the subshard is Android.
-      await _androidGradleTests(subShard);
-      break;
-    default:
-      await _runDevicelabTest('dartdocs');
-
-      if (Platform.isLinux) {
-        await _runDevicelabTest('flutter_create_offline_test_linux');
-      } else if (Platform.isWindows) {
-        await _runDevicelabTest('flutter_create_offline_test_windows');
-      } else if (Platform.isMacOS) {
-        await _runDevicelabTest('flutter_create_offline_test_mac');
-        await _runDevicelabTest('plugin_lint_mac');
-// TODO(jmagman): Re-enable once flakiness is resolved.
-//        await _runDevicelabTest('module_test_ios');
-      }
-  }
-}
-
-Future<void> _runDevicelabTest(String testName, {Map<String, String> env}) async {
-  await runCommand(
-    dart,
-    <String>['bin/run.dart', '-t', testName],
-    workingDirectory: path.join(flutterRoot, 'dev', 'devicelab'),
-    environment: env,
-  );
-}
-
-String get androidSdkRoot {
+Map<String, String> _initGradleEnvironment() {
   final String androidSdkRoot = (Platform.environment['ANDROID_HOME']?.isEmpty ?? true)
       ? Platform.environment['ANDROID_SDK_ROOT']
       : Platform.environment['ANDROID_HOME'];
   if (androidSdkRoot == null || androidSdkRoot.isEmpty) {
-    return null;
+    print('${red}Could not find Android SDK; set ANDROID_SDK_ROOT (or ANDROID_HOME).$reset');
+    exit(1);
   }
-  return androidSdkRoot;
-}
-
-Future<void> _androidGradleTests(String subShard) async {
-  // TODO(dnfield): gradlew is crashing on the cirrus image and it's not clear why.
-  if (androidSdkRoot == null || Platform.isWindows) {
-    print('No Android SDK detected or on Windows, skipping Android gradle test.');
-    return;
-  }
-  final Map<String, String> defaultEnv = <String, String>{
+  return <String, String>{
     'ANDROID_HOME': androidSdkRoot,
     'ANDROID_SDK_ROOT': androidSdkRoot,
-    'ENABLE_ANDROID_EMBEDDING_V2': Platform.environment['ENABLE_ANDROID_EMBEDDING_V2'] ?? '',
   };
-  if (subShard == 'gradle1') {
-    await _runDevicelabTest('gradle_plugin_light_apk_test', env: defaultEnv);
-    await _runDevicelabTest('gradle_plugin_fat_apk_test', env: defaultEnv);
-    await _runDevicelabTest('gradle_r8_test', env: defaultEnv);
-    await _runDevicelabTest('gradle_non_android_plugin_test', env: defaultEnv);
-    await _runDevicelabTest('gradle_jetifier_test', env: defaultEnv);
+}
+
+final Map<String, String> gradleEnvironment = _initGradleEnvironment();
+
+Future<void> _runHostOnlyDeviceLabTests() async {
+  if (Platform.isWindows) {
+    // TODO(ianh): remove when https://github.com/flutter/flutter/issues/36311 fixed by https://github.com/flutter/flutter/pull/42709
+    return;
   }
-  if (subShard == 'gradle2') {
-    await _runDevicelabTest('gradle_plugin_bundle_test', env: defaultEnv);
-    await _runDevicelabTest('module_test', env: defaultEnv);
-    await _runDevicelabTest('module_host_with_custom_build_test', env: defaultEnv);
-    await _runDevicelabTest('build_aar_module_test', env: defaultEnv);
-    await _runDevicelabTest('plugin_test', env: defaultEnv);
+
+  // Please don't add more tests here. We should not be using the devicelab
+  // logic to run tests outside devicelab, that's just confusing.
+  // Instead, create tests that are not devicelab tests, and run those.
+
+  // TODO(ianh): Move the tests that are not running on devicelab any more out
+  // of the device lab directory.
+
+  // List the tests to run.
+  // We split these into subshards. The tests are randomly distributed into
+  // those subshards so as to get a uniform distribution of costs, but the
+  // seed is fixed so that issues are reproducible.
+  final List<ShardRunner> tests = <ShardRunner>[
+    // Keep this in alphabetical order.
+    () => _runDevicelabTest('build_aar_module_test', environment: gradleEnvironment, testEmbeddingV2: true),
+    () => _runDevicelabTest('build_aar_module_test', environment: gradleEnvironment, testEmbeddingV2: false),
+    if (Platform.isMacOS) () => _runDevicelabTest('flutter_create_offline_test_mac'),
+    if (Platform.isLinux) () => _runDevicelabTest('flutter_create_offline_test_linux'),
+    if (Platform.isWindows) () => _runDevicelabTest('flutter_create_offline_test_windows'),
+    // TODO(ianh): Fails on macOS looking for "dexdump", https://github.com/flutter/flutter/issues/42494
+    if (!Platform.isMacOS) () => _runDevicelabTest('gradle_jetifier_test', environment: gradleEnvironment, testEmbeddingV2: false),
+    if (!Platform.isMacOS) () => _runDevicelabTest('gradle_jetifier_test', environment: gradleEnvironment, testEmbeddingV2: true),
+    () => _runDevicelabTest('gradle_non_android_plugin_test', environment: gradleEnvironment, testEmbeddingV2: false),
+    () => _runDevicelabTest('gradle_non_android_plugin_test', environment: gradleEnvironment, testEmbeddingV2: true),
+    () => _runDevicelabTest('gradle_plugin_bundle_test', environment: gradleEnvironment, testEmbeddingV2: false),
+    () => _runDevicelabTest('gradle_plugin_bundle_test', environment: gradleEnvironment, testEmbeddingV2: true),
+    () => _runDevicelabTest('gradle_plugin_fat_apk_test', environment: gradleEnvironment, testEmbeddingV2: false),
+    () => _runDevicelabTest('gradle_plugin_fat_apk_test', environment: gradleEnvironment, testEmbeddingV2: true),
+    () => _runDevicelabTest('gradle_plugin_light_apk_test', environment: gradleEnvironment, testEmbeddingV2: false),
+    () => _runDevicelabTest('gradle_plugin_light_apk_test', environment: gradleEnvironment, testEmbeddingV2: true),
+    () => _runDevicelabTest('gradle_r8_test', environment: gradleEnvironment, testEmbeddingV2: false),
+    () => _runDevicelabTest('gradle_r8_test', environment: gradleEnvironment, testEmbeddingV2: true),
+    () => _runDevicelabTest('module_host_with_custom_build_test', environment: gradleEnvironment, testEmbeddingV2: false),
+    () => _runDevicelabTest('module_host_with_custom_build_test', environment: gradleEnvironment, testEmbeddingV2: true),
+    () => _runDevicelabTest('module_test', environment: gradleEnvironment, testEmbeddingV2: false),
+    () => _runDevicelabTest('module_test', environment: gradleEnvironment, testEmbeddingV2: true),
+    // TODO(jmagman): Re-enable once flakiness is resolved, https://github.com/flutter/flutter/issues/37525
+    // if (Platform.isMacOS) () => _runDevicelabTest('module_test_ios'),
+    if (Platform.isMacOS) () => _runDevicelabTest('plugin_lint_mac'),
+    () => _runDevicelabTest('plugin_test', environment: gradleEnvironment, testEmbeddingV2: false),
+    () => _runDevicelabTest('plugin_test', environment: gradleEnvironment, testEmbeddingV2: true),
+  ]..shuffle(math.Random(0));
+
+  final int testsPerShard = tests.length ~/ kDeviceLabShardCount;
+  final Map<String, ShardRunner> subshards = <String, ShardRunner>{};
+
+  for (int subshard = 0; subshard < kDeviceLabShardCount; subshard += 1) {
+    String last = '';
+    List<ShardRunner> sublist;
+    if (subshard < kDeviceLabShardCount - 1) {
+      sublist = tests.sublist(subshard * testsPerShard, (subshard + 1) * testsPerShard);
+    } else {
+      sublist = tests.sublist(subshard * testsPerShard, tests.length);
+      // We make sure the last shard ends in _last so it's easier to catch mismatches
+      // between `.cirrus.yml` and `test.dart`.
+      last = '_last';
+    }
+    subshards['$subshard$last'] = () async {
+      for (ShardRunner test in sublist)
+        await test();
+    };
+  }
+
+  await selectSubshard(subshards);
+}
+
+Future<void> _runDevicelabTest(String testName, {
+  Map<String, String> environment,
+  bool testEmbeddingV2 = false,
+}) async {
+  await runCommand(
+    dart,
+    <String>['bin/run.dart', '-t', testName],
+    workingDirectory: path.join(flutterRoot, 'dev', 'devicelab'),
+    environment: <String, String>{
+      ...?environment,
+      if (testEmbeddingV2)
+        'ENABLE_ANDROID_EMBEDDING_V2': 'true',
+    },
+  );
+}
+
+void deleteFile(String path) {
+  // This is technically a race condition but nobody else should be running
+  // while this script runs, so we should be ok. (Sadly recursive:true does not
+  // obviate the need for existsSync, at least on Windows.)
+  final File file = File(path);
+  if (file.existsSync())
+    file.deleteSync();
+}
+
+enum CiProviders {
+  cirrus,
+  luci,
+}
+
+Future<void> _processTestOutput(
+  FlutterCompactFormatter formatter,
+  Stream<String> testOutput,
+  bq.TabledataResourceApi tableData,
+) async {
+  final Timer heartbeat = Timer.periodic(const Duration(seconds: 30), (Timer timer) {
+    print('Processing...');
+  });
+
+  await testOutput.forEach(formatter.processRawOutput);
+  heartbeat.cancel();
+  formatter.finish();
+  if (tableData == null || formatter.tests.isEmpty) {
+    return;
+  }
+  final bq.TableDataInsertAllRequest request = bq.TableDataInsertAllRequest();
+  final String authors = await _getAuthors();
+  request.rows = List<bq.TableDataInsertAllRequestRows>.from(
+    formatter.tests.map<bq.TableDataInsertAllRequestRows>((TestResult result) =>
+      bq.TableDataInsertAllRequestRows.fromJson(<String, dynamic> {
+        'json': <String, dynamic>{
+          'source': <String, dynamic>{
+            'provider': ciProviderName,
+            'url': ciUrl,
+            'platform': <String, dynamic>{
+              'os': Platform.operatingSystem,
+              'version': Platform.operatingSystemVersion,
+            },
+          },
+          'test': <String, dynamic>{
+            'name': result.name,
+            'result': result.status.toString(),
+            'file': result.path,
+            'line': result.line,
+            'column': result.column,
+            'time': result.totalTime,
+          },
+          'git': <String, dynamic>{
+            'author': authors,
+            'pull_request': prNumber,
+            'commit': gitHash,
+            'organization': 'flutter',
+            'repository': 'flutter',
+          },
+          'error': result.status != TestStatus.failed ? null : <String, dynamic>{
+            'message': result.errorMessage,
+            'stack_trace': result.stackTrace,
+          },
+          'information': result.messages,
+        },
+      }),
+    ),
+    growable: false,
+  );
+  final bq.TableDataInsertAllResponse response = await tableData.insertAll(request, 'flutter-infra', 'tests', 'ci');
+  if (response.insertErrors != null && response.insertErrors.isNotEmpty) {
+    print('${red}BigQuery insert errors:');
+    print(response.toJson());
+    print(reset);
   }
 }
 
-Future<void> selectShard(Map<String, ShardRunner> shards) => _runFromList(shards, 'SHARD', 'shard');
-Future<void> selectSubshard(Map<String, ShardRunner> subshards) => _runFromList(subshards, 'SUBSHARD', 'subshard');
+CiProviders get ciProvider {
+  if (Platform.environment['CIRRUS_CI'] == 'true') {
+    return CiProviders.cirrus;
+  }
+  if (Platform.environment['LUCI_CONTEXT'] != null) {
+    return CiProviders.luci;
+  }
+  return null;
+}
 
-Future<void> _runFromList(Map<String, ShardRunner> items, String key, String name) async {
-  final String item = Platform.environment[key];
-  if (item != null) {
+String get ciProviderName {
+  switch (ciProvider) {
+    case CiProviders.cirrus:
+      return 'cirrusci';
+    case CiProviders.luci:
+      return 'luci';
+  }
+  return 'unknown';
+}
+
+int get prNumber {
+  switch (ciProvider) {
+    case CiProviders.cirrus:
+      return Platform.environment['CIRRUS_PR'] == null
+          ? -1
+          : int.tryParse(Platform.environment['CIRRUS_PR']);
+    case CiProviders.luci:
+      return -1; // LUCI doesn't know about this.
+  }
+  return -1;
+}
+
+Future<String> _getAuthors() async {
+  final String exe = Platform.isWindows ? '.exe' : '';
+  final String author = await runAndGetStdout(
+    'git$exe', <String>['-c', 'log.showSignature=false', 'log', gitHash, '--pretty="%an <%ae>"'],
+    workingDirectory: flutterRoot,
+  ).first;
+  return author;
+}
+
+String get ciUrl {
+  switch (ciProvider) {
+    case CiProviders.cirrus:
+      return 'https://cirrus-ci.com/task/${Platform.environment['CIRRUS_TASK_ID']}';
+    case CiProviders.luci:
+      return 'https://ci.chromium.org/p/flutter/g/framework/console'; // TODO(dnfield): can we get a direct link to the actual build?
+  }
+  return '';
+}
+
+String get gitHash {
+  switch(ciProvider) {
+    case CiProviders.cirrus:
+      return Platform.environment['CIRRUS_CHANGE_IN_REPO'];
+    case CiProviders.luci:
+      return 'HEAD'; // TODO(dnfield): Set this in the env for LUCI.
+  }
+  return '';
+}
+
+/// Checks the given file's contents to determine if they match the allowed
+/// pattern for version strings.
+///
+/// Returns null if the contents are good. Returns a string if they are bad.
+/// The string is an error message.
+Future<String> verifyVersion(File file) async {
+  final RegExp pattern = RegExp(r'^\d+\.\d+\.\d+(\+hotfix\.\d+)?(-pre\.\d+)?$');
+  final String version = await file.readAsString();
+  if (!file.existsSync())
+    return 'The version logic failed to create the Flutter version file.';
+  if (version == '0.0.0-unknown')
+    return 'The version logic failed to determine the Flutter version.';
+  if (!version.contains(pattern))
+    return 'The version logic generated an invalid version string: "$version".';
+  return null;
+}
+
+/// If the CIRRUS_TASK_NAME environment variable exists, we use that to determine
+/// the shard and subshard (parsing it in the form shard-subshard-platform, ignoring
+/// the platform).
+///
+/// However, for local testing you can just set the SHARD and SUBSHARD
+/// environment variables. For example, to run all the framework tests you can
+/// just set SHARD=framework_tests. To run specifically the third subshard of
+/// the Web tests you can set SHARD=web_tests SUBSHARD=2 (it's zero-based).
+Future<void> selectShard(Map<String, ShardRunner> shards) => _runFromList(shards, 'SHARD', 'shard', 0);
+Future<void> selectSubshard(Map<String, ShardRunner> subshards) => _runFromList(subshards, 'SUBSHARD', 'subshard', 1);
+
+const String CIRRUS_TASK_NAME = 'CIRRUS_TASK_NAME';
+
+Future<void> _runFromList(Map<String, ShardRunner> items, String key, String name, int positionInTaskName) async {
+  String item = Platform.environment[key];
+  if (item == null && Platform.environment.containsKey(CIRRUS_TASK_NAME)) {
+    final List<String> parts = Platform.environment[CIRRUS_TASK_NAME].split('-');
+    assert(positionInTaskName < parts.length);
+    item = parts[positionInTaskName];
+  }
+  if (item == null) {
+    for (String currentItem in items.keys) {
+      print('$bold$key=$currentItem$reset');
+      await items[currentItem]();
+      print('');
+    }
+  } else {
     if (!items.containsKey(item)) {
       print('${red}Invalid $name: $item$reset');
       print('The available ${name}s are: ${items.keys.join(", ")}');
@@ -987,11 +996,5 @@
     }
     print('$bold$key=$item$reset');
     await items[item]();
-  } else {
-    for (String currentItem in items.keys) {
-      print('$bold$key=$currentItem$reset');
-      await items[currentItem]();
-      print('');
-    }
   }
 }
diff --git a/dev/bots/test/bot_test.dart b/dev/bots/test/bot_test.dart
new file mode 100644
index 0000000..476280c
--- /dev/null
+++ b/dev/bots/test/bot_test.dart
@@ -0,0 +1,13 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:io';
+
+import 'common.dart';
+
+void main() {
+  test('BOT variable is set on bots', () {
+    expect(Platform.environment['BOT'], 'true');
+  });
+}
diff --git a/dev/bots/test/sdk_directory_has_space_test.dart b/dev/bots/test/sdk_directory_has_space_test.dart
new file mode 100644
index 0000000..f48dbdb
--- /dev/null
+++ b/dev/bots/test/sdk_directory_has_space_test.dart
@@ -0,0 +1,21 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:io';
+
+import 'package:path/path.dart' as path;
+
+import 'common.dart';
+
+void main() {
+  test('We are in a directory with a space in it', () async {
+    // The Flutter SDK should be in a directory with a space in it, to make sure
+    // our tools support that.
+    final String expectedName = Platform.environment['FLUTTER_SDK_PATH_WITH_SPACE'];
+    expect(expectedName, 'flutter sdk');
+    expect(expectedName, contains(' '));
+    final List<String> parts = path.split(Directory.current.absolute.path);
+    expect(parts.reversed.take(3), <String>['bots', 'dev', expectedName]);
+  });
+}
diff --git a/dev/bots/test/test_test.dart b/dev/bots/test/test_test.dart
index a3abf6a..31df83f 100644
--- a/dev/bots/test/test_test.dart
+++ b/dev/bots/test/test_test.dart
@@ -29,7 +29,11 @@
       ];
       for (String version in valid_versions) {
         when(file.readAsString()).thenAnswer((Invocation invocation) => Future<String>.value(version));
-        expect(await verifyVersion(version, file), isTrue, reason: '$version is invalid');
+        expect(
+          await verifyVersion(file),
+          isNull,
+          reason: '$version is valid but verifyVersionFile said it was bad',
+        );
       }
     });
 
@@ -41,10 +45,15 @@
         '1.2.3-pre',
         '1.2.3-pre.1+hotfix.1',
         '  1.2.3',
+        '1.2.3-hotfix.1',
       ];
       for (String version in invalid_versions) {
         when(file.readAsString()).thenAnswer((Invocation invocation) => Future<String>.value(version));
-        expect(await verifyVersion(version, file), isFalse);
+        expect(
+          await verifyVersion(file),
+          'The version logic generated an invalid version string: "$version".',
+          reason: '$version is invalid but verifyVersionFile said it was fine',
+        );
       }
     });
   });
diff --git a/packages/flutter_tools/dart_test.yaml b/packages/flutter_tools/dart_test.yaml
index cd8c697..789a255 100644
--- a/packages/flutter_tools/dart_test.yaml
+++ b/packages/flutter_tools/dart_test.yaml
@@ -1,5 +1,9 @@
-tags:
-  "no_coverage":
-  "create":
-  "integration":
- 
+# package:test configuration
+# https://github.com/dart-lang/test/blob/master/pkgs/test/doc/configuration.md
+
+# Some of our tests take an absurdly long time to run, and on some
+# hosts they can take even longer due to the host suddenly being
+# overloaded. For this reason, we set the test timeout to
+# significantly more than it would be by default, and we never set the
+# timeouts in the tests themselves.
+timeout: 15m
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/ide_config_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/ide_config_test.dart
index abad543..4cf3b17 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/ide_config_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/ide_config_test.dart
@@ -135,7 +135,7 @@
       return _updateIdeConfig(
         expectedContents: expectedContents,
       );
-    }, timeout: const Timeout.factor(2.0));
+    });
 
     testUsingContext('creates non-existent files', () async {
       final Map<String, String> templateManifest = _getManifest(
@@ -155,7 +155,7 @@
       return _updateIdeConfig(
         expectedContents: expectedContents,
       );
-    }, timeout: const Timeout.factor(2.0));
+    });
 
     testUsingContext('overwrites existing files with --overwrite', () async {
       final Map<String, String> templateManifest = _getManifest(
@@ -181,7 +181,7 @@
         args: <String>['--overwrite'],
         expectedContents: expectedContents,
       );
-    }, timeout: const Timeout.factor(2.0));
+    });
 
     testUsingContext('only adds new templates without --overwrite', () async {
       final Map<String, String> templateManifest = _getManifest(
@@ -212,7 +212,7 @@
         args: <String>['--update-templates'],
         expectedContents: expectedContents,
       );
-    }, timeout: const Timeout.factor(2.0));
+    });
 
     testUsingContext('update all templates with --overwrite', () async {
       final Map<String, String> templateManifest = _getManifest(
@@ -239,7 +239,7 @@
         args: <String>['--update-templates', '--overwrite'],
         expectedContents: expectedContents,
       );
-    }, timeout: const Timeout.factor(2.0));
+    });
 
     testUsingContext('removes deleted imls with --overwrite', () async {
       final Map<String, String> templateManifest = _getManifest(
@@ -275,7 +275,7 @@
         args: <String>['--update-templates', '--overwrite'],
         expectedContents: expectedContents,
       );
-    }, timeout: const Timeout.factor(2.0));
+    });
 
     testUsingContext('removes deleted imls with --overwrite, including empty parent dirs', () async {
       final Map<String, String> templateManifest = _getManifest(
@@ -316,7 +316,7 @@
         args: <String>['--update-templates', '--overwrite'],
         expectedContents: expectedContents,
       );
-    }, timeout: const Timeout.factor(2.0));
+    });
 
   });
 }
diff --git a/packages/flutter_tools/test/commands.shard/permeable/analyze_once_test.dart b/packages/flutter_tools/test/commands.shard/permeable/analyze_once_test.dart
index 38b8980..b1c2a8a 100644
--- a/packages/flutter_tools/test/commands.shard/permeable/analyze_once_test.dart
+++ b/packages/flutter_tools/test/commands.shard/permeable/analyze_once_test.dart
@@ -16,9 +16,6 @@
 import '../../src/common.dart';
 import '../../src/context.dart';
 
-/// Test case timeout for tests involving project analysis.
-const Timeout allowForSlowAnalyzeTests = Timeout.factor(5.0);
-
 final Generator _kNoColorTerminalPlatform = () => FakePlatform.fromPlatform(const LocalPlatform())..stdoutSupportsAnsi = false;
 final Map<Type, Generator> noColorTerminalOverride = <Type, Generator>{
   Platform: _kNoColorTerminalPlatform,
@@ -54,7 +51,7 @@
         ],
       );
       expect(libMain.existsSync(), isTrue);
-    }, timeout: allowForRemotePubInvocation, overrides: <Type, Generator>{
+    }, overrides: <Type, Generator>{
       Pub: () => const Pub(),
     });
 
@@ -65,7 +62,7 @@
         arguments: <String>['analyze'],
         statusTextContains: <String>['No issues found!'],
       );
-    }, timeout: allowForSlowAnalyzeTests, overrides: <Type, Generator>{
+    }, overrides: <Type, Generator>{
       Pub: () => const Pub(),
     });
 
@@ -80,6 +77,7 @@
     }, overrides: <Type, Generator>{
       Pub: () => const Pub(),
     });
+
     // Analyze in the current directory - no arguments
     testUsingContext('working directory with errors', () async {
       // Break the code to produce the "The parameter 'onPressed' is required" hint
@@ -110,7 +108,7 @@
         exitMessageContains: '2 issues found.',
         toolExit: true,
       );
-    }, timeout: allowForSlowAnalyzeTests, overrides: <Type, Generator>{
+    }, overrides: <Type, Generator>{
       Pub: () => const Pub(),
       ...noColorTerminalOverride,
     });
@@ -140,7 +138,7 @@
         exitMessageContains: '3 issues found.',
         toolExit: true,
       );
-    }, timeout: allowForSlowAnalyzeTests, overrides: <Type, Generator>{
+    }, overrides: <Type, Generator>{
       Pub: () => const Pub(),
       ...noColorTerminalOverride
     });
diff --git a/packages/flutter_tools/test/commands.shard/permeable/build_bundle_test.dart b/packages/flutter_tools/test/commands.shard/permeable/build_bundle_test.dart
index 167326b..f732171 100644
--- a/packages/flutter_tools/test/commands.shard/permeable/build_bundle_test.dart
+++ b/packages/flutter_tools/test/commands.shard/permeable/build_bundle_test.dart
@@ -75,7 +75,7 @@
 
     expect(await command.usageValues,
         containsPair(CustomDimensions.commandBuildBundleIsModule, 'true'));
-  }, timeout: allowForCreateFlutterProject);
+  });
 
   testUsingContext('bundle getUsage indicate that project is not a module', () async {
     final String projectPath = await createProject(tempDir,
@@ -85,7 +85,7 @@
 
     expect(await command.usageValues,
         containsPair(CustomDimensions.commandBuildBundleIsModule, 'false'));
-  }, timeout: allowForCreateFlutterProject);
+  });
 
   testUsingContext('bundle getUsage indicate the target platform', () async {
     final String projectPath = await createProject(tempDir,
@@ -95,7 +95,7 @@
 
     expect(await command.usageValues,
         containsPair(CustomDimensions.commandBuildBundleTargetPlatform, 'android-arm'));
-  }, timeout: allowForCreateFlutterProject);
+  });
 
   testUsingContext('bundle fails to build for Windows if feature is disabled', () async {
     fs.file('lib/main.dart').createSync(recursive: true);
diff --git a/packages/flutter_tools/test/commands.shard/permeable/create_test.dart b/packages/flutter_tools/test/commands.shard/permeable/create_test.dart
index 2056e49..6165895 100644
--- a/packages/flutter_tools/test/commands.shard/permeable/create_test.dart
+++ b/packages/flutter_tools/test/commands.shard/permeable/create_test.dart
@@ -78,7 +78,7 @@
       ],
     );
     return _runFlutterTest(projectDir);
-  }, timeout: allowForRemotePubInvocation, overrides: <Type, Generator>{
+  }, overrides: <Type, Generator>{
     Pub: () => const Pub(),
   });
 
@@ -96,7 +96,7 @@
         'ios/Runner/GeneratedPluginRegistrant.h',
       ],
     );
-  }, timeout: allowForRemotePubInvocation, overrides: <Type, Generator>{
+  }, overrides: <Type, Generator>{
     Pub: () => const Pub(),
   });
 
@@ -118,11 +118,9 @@
       'ios/',
     ]);
     return _runFlutterTest(projectDir);
-  },
-    timeout: allowForRemotePubInvocation, overrides: <Type, Generator>{
-      Pub: () => const Pub(),
-    },
-  );
+  }, overrides: <Type, Generator>{
+    Pub: () => const Pub(),
+  });
 
   testUsingContext('cannot create a project if non-empty non-project directory exists with .metadata', () async {
     await projectDir.absolute.childDirectory('blag').create(recursive: true);
@@ -138,7 +136,7 @@
           '.ios/',
         ]),
       throwsToolExit(message: 'Sorry, unable to detect the type of project to recreate'));
-  }, timeout: allowForRemotePubInvocation, overrides: <Type, Generator>{
+  }, overrides: <Type, Generator>{
     Pub: () => const Pub(),
     ...noColorTerminalOverride,
   });
@@ -164,7 +162,7 @@
         '.ios/',
       ],
     );
-  }, timeout: allowForRemotePubInvocation, overrides: <Type, Generator>{
+  }, overrides: <Type, Generator>{
     Pub: () => const Pub(),
   });
 
@@ -189,7 +187,7 @@
         '.ios/',
       ],
     );
-  }, timeout: allowForRemotePubInvocation, overrides: <Type, Generator>{
+  }, overrides: <Type, Generator>{
     Pub: () => const Pub(),
   });
 
@@ -214,7 +212,7 @@
         'lib/flutter_project.dart',
       ],
     );
-  }, timeout: allowForRemotePubInvocation, overrides: <Type, Generator>{
+  }, overrides: <Type, Generator>{
     Pub: () => const Pub(),
   });
 
@@ -245,7 +243,7 @@
         'test/widget_test.dart',
       ],
     );
-  }, timeout: allowForRemotePubInvocation, overrides: <Type, Generator>{
+  }, overrides: <Type, Generator>{
     Pub: () => const Pub(),
   });
 
@@ -267,7 +265,7 @@
         'ios/Runner/main.m',
       ],
     );
-  }, timeout: allowForCreateFlutterProject, overrides: <Type, Generator>{
+  }, overrides: <Type, Generator>{
     Pub: () => const Pub(),
   });
 
@@ -297,7 +295,7 @@
       ],
     );
     return _runFlutterTest(projectDir);
-  }, timeout: allowForRemotePubInvocation, overrides: <Type, Generator>{
+  }, overrides: <Type, Generator>{
     Pub: () => const Pub(),
   });
 
@@ -319,7 +317,7 @@
       ],
     );
     return _runFlutterTest(projectDir.childDirectory('example'));
-  }, timeout: allowForRemotePubInvocation, overrides: <Type, Generator>{
+  }, overrides: <Type, Generator>{
     Pub: () => const Pub(),
   });
 
@@ -346,7 +344,7 @@
         'example/ios/Runner/main.m',
       ],
     );
-  }, timeout: allowForCreateFlutterProject);
+  });
 
   testUsingContext('plugin project with custom org', () async {
     return _createProject(
@@ -366,7 +364,7 @@
         'example/android/app/src/main/java/com/example/flutter_project_example/MainActivity.java',
       ],
     );
-  }, timeout: allowForCreateFlutterProject);
+  });
 
   testUsingContext('plugin project with valid custom project name', () async {
     return _createProject(
@@ -386,7 +384,7 @@
         'example/android/app/src/main/java/com/example/flutter_project_example/MainActivity.java',
       ],
     );
-  }, timeout: allowForCreateFlutterProject);
+  });
 
   testUsingContext('plugin project with invalid custom project name', () async {
     expect(
@@ -396,7 +394,7 @@
       ),
       throwsToolExit(message: '"xyz.xyz" is not a valid Dart package name.'),
     );
-  }, timeout: allowForCreateFlutterProject);
+  });
 
   testUsingContext('legacy app project with-driver-test', () async {
     return _createAndAnalyzeProject(
@@ -404,7 +402,7 @@
       <String>['--with-driver-test', '--template=app'],
       <String>['lib/main.dart'],
     );
-  }, timeout: allowForRemotePubInvocation, overrides: <Type, Generator>{
+  }, overrides: <Type, Generator>{
     Pub: () => const Pub(),
   });
 
@@ -439,7 +437,7 @@
       'android/',
       'ios/',
     ]);
-  }, timeout: allowForRemotePubInvocation, overrides: <Type, Generator>{
+  }, overrides: <Type, Generator>{
     Pub: () => const Pub(),
   });
 
@@ -463,7 +461,7 @@
     final String actualContents = await fs.file(projectDir.path + '/android/gradle.properties').readAsString();
 
     expect(actualContents.contains('useAndroidX'), true);
-  }, timeout: allowForCreateFlutterProject);
+  });
 
   testUsingContext('non androidx app project', () async {
     Cache.flutterRoot = '../..';
@@ -484,7 +482,7 @@
     final String actualContents = await fs.file(projectDir.path + '/android/gradle.properties').readAsString();
 
     expect(actualContents.contains('useAndroidX'), false);
-  }, timeout: allowForCreateFlutterProject);
+  });
 
   testUsingContext('androidx is used by default in a module project', () async {
     Cache.flutterRoot = '../..';
@@ -501,7 +499,7 @@
       project.usesAndroidX,
       true,
     );
-  }, timeout: allowForCreateFlutterProject);
+  });
 
   testUsingContext('non androidx module', () async {
     Cache.flutterRoot = '../..';
@@ -518,7 +516,7 @@
       project.usesAndroidX,
       false,
     );
-  }, timeout: allowForCreateFlutterProject);
+  });
 
   testUsingContext('androidx is used by default in a plugin project', () async {
     Cache.flutterRoot = '../..';
@@ -539,7 +537,7 @@
     final String actualContents = await fs.file(projectDir.path + '/android/gradle.properties').readAsString();
 
     expect(actualContents.contains('useAndroidX'), true);
-  }, timeout: allowForCreateFlutterProject);
+  });
 
   testUsingContext('non androidx plugin project', () async {
     Cache.flutterRoot = '../..';
@@ -560,7 +558,7 @@
     final String actualContents = await fs.file(projectDir.path + '/android/gradle.properties').readAsString();
 
     expect(actualContents.contains('useAndroidX'), false);
-  }, timeout: allowForCreateFlutterProject);
+  });
 
   testUsingContext('app supports macOS if requested', () async {
     Cache.flutterRoot = '../..';
@@ -573,7 +571,7 @@
     await runner.run(<String>['create', '--no-pub', '--macos', projectDir.path]);
 
     expect(projectDir.childDirectory('macos').childDirectory('Runner.xcworkspace').existsSync(), true);
-  }, timeout: allowForCreateFlutterProject);
+  });
 
   testUsingContext('app does not include macOS by default', () async {
     Cache.flutterRoot = '../..';
@@ -586,7 +584,7 @@
     await runner.run(<String>['create', '--no-pub', projectDir.path]);
 
     expect(projectDir.childDirectory('macos').childDirectory('Runner.xcworkspace').existsSync(), false);
-  }, timeout: allowForCreateFlutterProject);
+  });
 
   testUsingContext('plugin supports macOS if requested', () async {
     Cache.flutterRoot = '../..';
@@ -599,7 +597,7 @@
     await runner.run(<String>['create', '--no-pub', '--template=plugin', '--macos', projectDir.path]);
 
     expect(projectDir.childDirectory('macos').childFile('flutter_project.podspec').existsSync(), true);
-  }, timeout: allowForCreateFlutterProject);
+  });
 
   testUsingContext('plugin does not include macOS by default', () async {
     Cache.flutterRoot = '../..';
@@ -612,7 +610,7 @@
     await runner.run(<String>['create', '--no-pub', '--template=plugin', projectDir.path]);
 
     expect(projectDir.childDirectory('macos').childFile('flutter_project.podspec').existsSync(), false);
-  }, timeout: allowForCreateFlutterProject);
+  });
 
   testUsingContext('has correct content and formatting with module template', () async {
     Cache.flutterRoot = '../..';
@@ -708,7 +706,7 @@
   }, overrides: <Type, Generator>{
     FlutterVersion: () => mockFlutterVersion,
     Platform: _kNoColorTerminalPlatform,
-  }, timeout: allowForCreateFlutterProject);
+  });
 
   testUsingContext('has correct content and formatting with app template', () async {
     Cache.flutterRoot = '../..';
@@ -779,7 +777,7 @@
   }, overrides: <Type, Generator>{
     FlutterVersion: () => mockFlutterVersion,
     Platform: _kNoColorTerminalPlatform,
-  }, timeout: allowForCreateFlutterProject);
+  });
 
   testUsingContext('has correct application id for android and bundle id for ios', () async {
     Cache.flutterRoot = '../..';
@@ -827,7 +825,7 @@
   }, overrides: <Type, Generator>{
     FlutterVersion: () => mockFlutterVersion,
     Platform: _kNoColorTerminalPlatform,
-  }, timeout: allowForCreateFlutterProject);
+  });
 
   testUsingContext('can re-gen default template over existing project', () async {
     Cache.flutterRoot = '../..';
@@ -841,7 +839,7 @@
 
     final String metadata = fs.file(fs.path.join(projectDir.path, '.metadata')).readAsStringSync();
     expect(metadata, contains('project_type: app\n'));
-  }, timeout: allowForCreateFlutterProject);
+  });
 
   testUsingContext('can re-gen default template over existing app project with no metadta and detect the type', () async {
     Cache.flutterRoot = '../..';
@@ -858,7 +856,7 @@
 
     final String metadata = fs.file(fs.path.join(projectDir.path, '.metadata')).readAsStringSync();
     expect(metadata, contains('project_type: app\n'));
-  }, timeout: allowForCreateFlutterProject);
+  });
 
   testUsingContext('can re-gen app template over existing app project and detect the type', () async {
     Cache.flutterRoot = '../..';
@@ -872,7 +870,7 @@
 
     final String metadata = fs.file(fs.path.join(projectDir.path, '.metadata')).readAsStringSync();
     expect(metadata, contains('project_type: app\n'));
-  }, timeout: allowForCreateFlutterProject);
+  });
 
   testUsingContext('can re-gen template over existing module project and detect the type', () async {
     Cache.flutterRoot = '../..';
@@ -886,7 +884,7 @@
 
     final String metadata = fs.file(fs.path.join(projectDir.path, '.metadata')).readAsStringSync();
     expect(metadata, contains('project_type: module\n'));
-  }, timeout: allowForCreateFlutterProject);
+  });
 
   testUsingContext('can re-gen default template over existing plugin project and detect the type', () async {
     Cache.flutterRoot = '../..';
@@ -900,7 +898,7 @@
 
     final String metadata = fs.file(fs.path.join(projectDir.path, '.metadata')).readAsStringSync();
     expect(metadata, contains('project_type: plugin'));
-  }, timeout: allowForCreateFlutterProject);
+  });
 
   testUsingContext('can re-gen default template over existing package project and detect the type', () async {
     Cache.flutterRoot = '../..';
@@ -914,7 +912,7 @@
 
     final String metadata = fs.file(fs.path.join(projectDir.path, '.metadata')).readAsStringSync();
     expect(metadata, contains('project_type: package'));
-  }, timeout: allowForCreateFlutterProject);
+  });
 
   testUsingContext('can re-gen module .android/ folder, reusing custom org', () async {
     await _createProject(
@@ -930,7 +928,7 @@
         '.android/app/src/main/java/com/bar/foo/flutter_project/host/MainActivity.java',
       ],
     );
-  }, timeout: allowForRemotePubInvocation, overrides: <Type, Generator>{
+  }, overrides: <Type, Generator>{
     Pub: () => const Pub(),
   });
 
@@ -947,7 +945,7 @@
       await project.ios.productBundleIdentifier,
       'com.bar.foo.flutterProject',
     );
-  }, timeout: allowForRemotePubInvocation, overrides: <Type, Generator>{
+  }, overrides: <Type, Generator>{
     Pub: () => const Pub(),
   });
 
@@ -974,7 +972,7 @@
         'android/app/src/main/java/com/example/flutter_project/MainActivity.java',
       ],
     );
-  }, timeout: allowForCreateFlutterProject);
+  });
 
   testUsingContext('can re-gen app ios/ folder, reusing custom org', () async {
     await _createProject(
@@ -989,7 +987,7 @@
       await project.ios.productBundleIdentifier,
       'com.bar.foo.flutterProject',
     );
-  }, timeout: allowForCreateFlutterProject);
+  });
 
   testUsingContext('can re-gen plugin ios/ and example/ folders, reusing custom org', () async {
     await _createProject(
@@ -1022,7 +1020,7 @@
       await project.example.ios.productBundleIdentifier,
       'com.bar.foo.flutterProjectExample',
     );
-  }, timeout: allowForCreateFlutterProject);
+  });
 
   testUsingContext('fails to re-gen without specified org when org is ambiguous', () async {
     await _createProject(
@@ -1040,7 +1038,7 @@
       () => _createProject(projectDir, <String>[], <String>[]),
       throwsToolExit(message: 'Ambiguous organization'),
     );
-  }, timeout: allowForCreateFlutterProject);
+  });
 
   // Verify that we help the user correct an option ordering issue
   testUsingContext('produces sensible error message', () async {
@@ -1102,7 +1100,7 @@
         'ios/Runner/GeneratedPluginRegistrant.h',
       ],
     );
-  }, timeout: allowForRemotePubInvocation, overrides: <Type, Generator>{
+  }, overrides: <Type, Generator>{
     Pub: () => const Pub(),
   });
 
@@ -1128,7 +1126,6 @@
       expect(loggingProcessManager.commands.first, contains(matches(r'dart-sdk[\\/]bin[\\/]pub')));
       expect(loggingProcessManager.commands.first, contains('--offline'));
     },
-    timeout: allowForCreateFlutterProject,
     overrides: <Type, Generator>{
       ProcessManager: () => loggingProcessManager,
       Pub: () => const Pub(),
@@ -1147,7 +1144,6 @@
       expect(loggingProcessManager.commands.first, contains(matches(r'dart-sdk[\\/]bin[\\/]pub')));
       expect(loggingProcessManager.commands.first, isNot(contains('--offline')));
     },
-    timeout: allowForCreateFlutterProject,
     overrides: <Type, Generator>{
       ProcessManager: () => loggingProcessManager,
       Pub: () => const Pub(),
@@ -1168,7 +1164,7 @@
     );
     expect(projectDir.childDirectory('lib').childFile('main.dart').readAsStringSync(),
       contains('void main() {}'));
-  }, timeout: allowForRemotePubInvocation, overrides: <Type, Generator>{
+  }, overrides: <Type, Generator>{
     HttpClientFactory: () => () => MockHttpClient(200, result: 'void main() {}'),
   });
 
diff --git a/packages/flutter_tools/test/commands.shard/permeable/packages_test.dart b/packages/flutter_tools/test/commands.shard/permeable/packages_test.dart
index 07da823..a9197ed 100644
--- a/packages/flutter_tools/test/commands.shard/permeable/packages_test.dart
+++ b/packages/flutter_tools/test/commands.shard/permeable/packages_test.dart
@@ -207,7 +207,7 @@
 
       expectDependenciesResolved(projectPath);
       expectZeroPluginsInjected(projectPath);
-    }, timeout: allowForRemotePubInvocation, overrides: <Type, Generator>{
+    }, overrides: <Type, Generator>{
       Pub: () => const Pub(),
     });
 
@@ -220,7 +220,7 @@
 
       expectDependenciesResolved(projectPath);
       expectZeroPluginsInjected(projectPath);
-    }, timeout: allowForCreateFlutterProject, overrides: <Type, Generator>{
+    }, overrides: <Type, Generator>{
       Pub: () => const Pub(),
     });
 
@@ -234,7 +234,7 @@
 
       expect(await getCommand.usageValues,
              containsPair(CustomDimensions.commandPackagesNumberPlugins, '0'));
-    }, timeout: allowForCreateFlutterProject, overrides: <Type, Generator>{
+    }, overrides: <Type, Generator>{
       Pub: () => const Pub(),
     });
 
@@ -248,7 +248,7 @@
 
       expect(await getCommand.usageValues,
              containsPair(CustomDimensions.commandPackagesProjectModule, 'false'));
-    }, timeout: allowForCreateFlutterProject, overrides: <Type, Generator>{
+    }, overrides: <Type, Generator>{
       Pub: () => const Pub(),
     });
 
@@ -262,7 +262,7 @@
 
       expect(await getCommand.usageValues,
              containsPair(CustomDimensions.commandPackagesProjectModule, 'true'));
-    }, timeout: allowForCreateFlutterProject, overrides: <Type, Generator>{
+    }, overrides: <Type, Generator>{
       Pub: () => const Pub(),
     });
 
@@ -275,7 +275,7 @@
 
       expectDependenciesResolved(projectPath);
       expectZeroPluginsInjected(projectPath);
-    }, timeout: allowForRemotePubInvocation, overrides: <Type, Generator>{
+    }, overrides: <Type, Generator>{
       Pub: () => const Pub(),
     });
 
@@ -288,7 +288,7 @@
 
       expectDependenciesResolved(projectPath);
       expectModulePluginInjected(projectPath);
-    }, timeout: allowForRemotePubInvocation, overrides: <Type, Generator>{
+    }, overrides: <Type, Generator>{
       Pub: () => const Pub(),
     });
 
@@ -309,7 +309,7 @@
 
       expectDependenciesResolved(exampleProjectPath);
       expectPluginInjected(exampleProjectPath);
-    }, timeout: allowForRemotePubInvocation, overrides: <Type, Generator>{
+    }, overrides: <Type, Generator>{
       Pub: () => const Pub(),
     });
   });
diff --git a/packages/flutter_tools/test/general.shard/commands/build_aar_test.dart b/packages/flutter_tools/test/general.shard/commands/build_aar_test.dart
index 1591e75..a93b50e 100644
--- a/packages/flutter_tools/test/general.shard/commands/build_aar_test.dart
+++ b/packages/flutter_tools/test/general.shard/commands/build_aar_test.dart
@@ -57,7 +57,7 @@
 
     }, overrides: <Type, Generator>{
       AndroidBuilder: () => FakeAndroidBuilder(),
-    }, timeout: allowForCreateFlutterProject);
+    });
 
     testUsingContext('indicate that project is a plugin', () async {
       final String projectPath = await createProject(tempDir,
@@ -69,7 +69,7 @@
 
     }, overrides: <Type, Generator>{
       AndroidBuilder: () => FakeAndroidBuilder(),
-    }, timeout: allowForCreateFlutterProject);
+    });
 
     testUsingContext('indicate the target platform', () async {
       final String projectPath = await createProject(tempDir,
@@ -82,7 +82,7 @@
 
     }, overrides: <Type, Generator>{
       AndroidBuilder: () => FakeAndroidBuilder(),
-    }, timeout: allowForCreateFlutterProject);
+    });
   });
 
   group('Gradle', () {
diff --git a/packages/flutter_tools/test/general.shard/commands/build_apk_test.dart b/packages/flutter_tools/test/general.shard/commands/build_apk_test.dart
index a67c9fa..05fa1ff 100644
--- a/packages/flutter_tools/test/general.shard/commands/build_apk_test.dart
+++ b/packages/flutter_tools/test/general.shard/commands/build_apk_test.dart
@@ -48,7 +48,7 @@
 
     }, overrides: <Type, Generator>{
       AndroidBuilder: () => FakeAndroidBuilder(),
-    }, timeout: allowForCreateFlutterProject);
+    });
 
     testUsingContext('split per abi', () async {
       final String projectPath = await createProject(tempDir,
@@ -65,7 +65,7 @@
 
     }, overrides: <Type, Generator>{
       AndroidBuilder: () => FakeAndroidBuilder(),
-    }, timeout: allowForCreateFlutterProject);
+    });
 
     testUsingContext('build type', () async {
       final String projectPath = await createProject(tempDir,
@@ -92,7 +92,7 @@
 
     }, overrides: <Type, Generator>{
       AndroidBuilder: () => FakeAndroidBuilder(),
-    }, timeout: allowForCreateFlutterProject);
+    });
   });
 
   group('Gradle', () {
@@ -223,8 +223,7 @@
       FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
       GradleUtils: () => GradleUtils(),
       ProcessManager: () => mockProcessManager,
-    },
-    timeout: allowForCreateFlutterProject);
+    });
 
     testUsingContext('shrinking is disabled when --no-shrink is passed', () async {
       final String projectPath = await createProject(tempDir,
@@ -255,8 +254,7 @@
       FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
       GradleUtils: () => GradleUtils(),
       ProcessManager: () => mockProcessManager,
-    },
-    timeout: allowForCreateFlutterProject);
+    });
 
     testUsingContext('guides the user when the shrinker fails', () async {
       final String projectPath = await createProject(tempDir,
@@ -312,8 +310,7 @@
       FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
       ProcessManager: () => mockProcessManager,
       Usage: () => mockUsage,
-    },
-    timeout: allowForCreateFlutterProject);
+    });
 
     testUsingContext('reports when the app isn\'t using AndroidX', () async {
       final String projectPath = await createProject(tempDir,
@@ -366,8 +363,7 @@
       FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
       ProcessManager: () => mockProcessManager,
       Usage: () => mockUsage,
-    },
-    timeout: allowForCreateFlutterProject);
+    });
 
     testUsingContext('reports when the app is using AndroidX', () async {
       final String projectPath = await createProject(tempDir,
@@ -422,8 +418,7 @@
       FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
       ProcessManager: () => mockProcessManager,
       Usage: () => mockUsage,
-    },
-    timeout: allowForCreateFlutterProject);
+    });
   });
 }
 
diff --git a/packages/flutter_tools/test/general.shard/commands/build_appbundle_test.dart b/packages/flutter_tools/test/general.shard/commands/build_appbundle_test.dart
index a00d279..7663156 100644
--- a/packages/flutter_tools/test/general.shard/commands/build_appbundle_test.dart
+++ b/packages/flutter_tools/test/general.shard/commands/build_appbundle_test.dart
@@ -48,7 +48,7 @@
 
     }, overrides: <Type, Generator>{
       AndroidBuilder: () => FakeAndroidBuilder(),
-    }, timeout: allowForCreateFlutterProject);
+    });
 
     testUsingContext('build type', () async {
       final String projectPath = await createProject(tempDir,
@@ -75,7 +75,7 @@
 
     }, overrides: <Type, Generator>{
       AndroidBuilder: () => FakeAndroidBuilder(),
-    }, timeout: allowForCreateFlutterProject);
+    });
   });
 
   group('Gradle', () {
@@ -212,8 +212,7 @@
       FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
       GradleUtils: () => GradleUtils(),
       ProcessManager: () => mockProcessManager,
-    },
-    timeout: allowForCreateFlutterProject);
+    });
 
     testUsingContext('shrinking is disabled when --no-shrink is passed', () async {
       final String projectPath = await createProject(
@@ -246,8 +245,7 @@
       FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
       GradleUtils: () => GradleUtils(),
       ProcessManager: () => mockProcessManager,
-    },
-    timeout: allowForCreateFlutterProject);
+    });
 
     testUsingContext('guides the user when the shrinker fails', () async {
       final String projectPath = await createProject(tempDir,
@@ -303,8 +301,7 @@
       FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
       ProcessManager: () => mockProcessManager,
       Usage: () => mockUsage,
-    },
-    timeout: allowForCreateFlutterProject);
+    });
 
     testUsingContext('reports when the app isn\'t using AndroidX', () async {
       final String projectPath = await createProject(tempDir,
@@ -357,8 +354,7 @@
       FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
       ProcessManager: () => mockProcessManager,
       Usage: () => mockUsage,
-    },
-    timeout: allowForCreateFlutterProject);
+    });
 
     testUsingContext('reports when the app is using AndroidX', () async {
       final String projectPath = await createProject(tempDir,
@@ -413,8 +409,7 @@
       FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir),
       ProcessManager: () => mockProcessManager,
       Usage: () => mockUsage,
-    },
-    timeout: allowForCreateFlutterProject);
+    });
   });
 }
 
diff --git a/packages/flutter_tools/test/integration.shard/daemon_mode_test.dart b/packages/flutter_tools/test/integration.shard/daemon_mode_test.dart
index f1ce5ba..aee21d0 100644
--- a/packages/flutter_tools/test/integration.shard/daemon_mode_test.dart
+++ b/packages/flutter_tools/test/integration.shard/daemon_mode_test.dart
@@ -2,13 +2,9 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Integration tests which invoke flutter instead of unit testing the code
-// will not produce meaningful coverage information - we can measure coverage
-// from the isolate running the test, but not from the isolate started via
-// the command line process.
-@Tags(<String>['no_coverage'])
 import 'dart:async';
 import 'dart:convert';
+import 'dart:io';
 
 import 'package:file/file.dart';
 import 'package:flutter_tools/src/base/file_system.dart';
@@ -21,79 +17,58 @@
 import 'test_utils.dart';
 
 void main() {
-  group('daemon_mode', () {
-    Directory tempDir;
+  test('device.getDevices', () async {
+    final Directory tempDir = createResolvedTempDirectorySync('daemon_mode_test.');
+
     final BasicProject _project = BasicProject();
-    Process process;
+    await _project.setUpIn(tempDir);
 
-    setUp(() async {
-      tempDir = createResolvedTempDirectorySync('daemon_mode_test.');
-      await _project.setUpIn(tempDir);
-    });
+    final String flutterBin = fs.path.join(getFlutterRoot(), 'bin', 'flutter');
 
-    tearDown(() async {
-      tryToDelete(tempDir);
-      process?.kill();
-    });
+    const ProcessManager processManager = LocalProcessManager();
+    final Process process = await processManager.start(
+      <String>[flutterBin, '--show-test-device', 'daemon'],
+      workingDirectory: tempDir.path,
+    );
 
-    test('device.getDevices', () async {
-      final String flutterBin =
-          fs.path.join(getFlutterRoot(), 'bin', 'flutter');
+    final StreamController<String> stdout = StreamController<String>.broadcast();
+    transformToLines(process.stdout).listen((String line) => stdout.add(line));
+    final Stream<Map<String, dynamic>> stream = stdout
+      .stream
+      .map<Map<String, dynamic>>(parseFlutterResponse)
+      .where((Map<String, dynamic> value) => value != null);
 
-      const ProcessManager processManager = LocalProcessManager();
-      process = await processManager.start(
-          <String>[flutterBin, '--show-test-device', 'daemon'],
-          workingDirectory: tempDir.path);
+    Map<String, dynamic> response = await stream.first;
+    expect(response['event'], 'daemon.connected');
 
-      final StreamController<String> stdout =
-          StreamController<String>.broadcast();
+    // start listening for devices
+    process.stdin.writeln('[${jsonEncode(<String, dynamic>{
+      'id': 1,
+      'method': 'device.enable',
+    })}]');
+    response = await stream.first;
+    expect(response['id'], 1);
+    expect(response['error'], isNull);
 
-      transformToLines(process.stdout)
-          .listen((String line) => stdout.add(line));
+    // [{"event":"device.added","params":{"id":"flutter-tester","name":
+    //   "Flutter test device","platform":"flutter-tester","emulator":false}}]
+    response = await stream.first;
+    expect(response['event'], 'device.added');
 
-      final Stream<Map<String, dynamic>> stream =
-        stdout.stream.where((String line) {
-          final Map<String, dynamic> response = parseFlutterResponse(line);
-          // ignore 'Starting device daemon...'
-          if (response == null) {
-            return false;
-          }
-          // TODO(devoncarew): Remove this after #25440 lands.
-          if (response['event'] == 'daemon.showMessage') {
-            return false;
-          }
-          return true;
-        }).map(parseFlutterResponse);
+    // get the list of all devices
+    process.stdin.writeln('[${jsonEncode(<String, dynamic>{
+      'id': 2,
+      'method': 'device.getDevices',
+    })}]');
+    response = await stream.first;
+    expect(response['id'], 2);
+    expect(response['error'], isNull);
 
-      Map<String, dynamic> response = await stream.first;
-      expect(response['event'], 'daemon.connected');
+    final dynamic result = response['result'];
+    expect(result, isList);
+    expect(result, isNotEmpty);
 
-      // start listening for devices
-      process.stdin.writeln('[${jsonEncode(<String, dynamic>{
-        'id': 1,
-        'method': 'device.enable',
-      })}]');
-      response = await stream.first;
-      expect(response['id'], 1);
-      expect(response['error'], isNull);
-
-      // [{"event":"device.added","params":{"id":"flutter-tester","name":
-      //   "Flutter test device","platform":"flutter-tester","emulator":false}}]
-      response = await stream.first;
-      expect(response['event'], 'device.added');
-
-      // get the list of all devices
-      process.stdin.writeln('[${jsonEncode(<String, dynamic>{
-        'id': 2,
-        'method': 'device.getDevices',
-      })}]');
-      response = await stream.first;
-      expect(response['id'], 2);
-      expect(response['error'], isNull);
-
-      final dynamic result = response['result'];
-      expect(result, isList);
-      expect(result, isNotEmpty);
-    });
-  }, timeout: const Timeout.factor(10), tags: <String>['integration']); // This test uses the `flutter` tool, which could be blocked behind the startup lock for a long time.
+    tryToDelete(tempDir);
+    process.kill();
+  });
 }
diff --git a/packages/flutter_tools/test/integration.shard/debugger_stepping_test.dart b/packages/flutter_tools/test/integration.shard/debugger_stepping_test.dart
index a53cff9..e6a743c 100644
--- a/packages/flutter_tools/test/integration.shard/debugger_stepping_test.dart
+++ b/packages/flutter_tools/test/integration.shard/debugger_stepping_test.dart
@@ -2,11 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Integration tests which invoke flutter instead of unit testing the code
-// will not produce meaningful coverage information - we can measure coverage
-// from the isolate running the test, but not from the isolate started via
-// the command line process.
-@Tags(<String>['no_coverage'])
+import 'dart:io';
+
 import 'package:file/file.dart';
 import 'package:flutter_tools/src/base/file_system.dart';
 
@@ -16,43 +13,37 @@
 import 'test_utils.dart';
 
 void main() {
-  group('debugger', () {
-    Directory tempDir;
+  test('can step over statements', () async {
+    final Directory tempDir = createResolvedTempDirectorySync('debugger_stepping_test.');
+
     final SteppingProject _project = SteppingProject();
-    FlutterRunTestDriver _flutter;
+    await _project.setUpIn(tempDir);
 
-    setUp(() async {
-      tempDir = createResolvedTempDirectorySync('debugger_stepping_test.');
-      await _project.setUpIn(tempDir);
-      _flutter = FlutterRunTestDriver(tempDir);
-    });
+    final FlutterRunTestDriver _flutter = FlutterRunTestDriver(tempDir);
 
-    tearDown(() async {
-      await _flutter.stop();
-      tryToDelete(tempDir);
-    });
+    await _flutter.run(withDebugger: true, startPaused: true);
+    await _flutter.addBreakpoint(_project.breakpointUri, _project.breakpointLine);
+    await _flutter.resume();
+    await _flutter.waitForPause(); // Now we should be on the breakpoint.
 
-    test('can step over statements', () async {
-      await _flutter.run(withDebugger: true, startPaused: true);
-      await _flutter.addBreakpoint(_project.breakpointUri, _project.breakpointLine);
-      await _flutter.resume();
-      await _flutter.waitForPause(); // Now we should be on the breakpoint.
+    expect((await _flutter.getSourceLocation()).line, equals(_project.breakpointLine));
 
-      expect((await _flutter.getSourceLocation()).line, equals(_project.breakpointLine));
+    // Issue 5 steps, ensuring that we end up on the annotated lines each time.
+    for (int i = 1; i <= _project.numberOfSteps; i += 1) {
+      await _flutter.stepOverOrOverAsyncSuspension();
+      final SourcePosition location = await _flutter.getSourceLocation();
+      final int actualLine = location.line;
 
-      // Issue 5 steps, ensuring that we end up on the annotated lines each time.
-      for (int i = 1; i <= _project.numberOfSteps; i += 1) {
-        await _flutter.stepOverOrOverAsyncSuspension();
-        final SourcePosition location = await _flutter.getSourceLocation();
-        final int actualLine = location.line;
+      // Get the line we're expected to stop at by searching for the comment
+      // within the source code.
+      final int expectedLine = _project.lineForStep(i);
 
-        // Get the line we're expected to stop at by searching for the comment
-        // within the source code.
-        final int expectedLine = _project.lineForStep(i);
+      expect(actualLine, equals(expectedLine),
+        reason: 'After $i steps, debugger should stop at $expectedLine but stopped at $actualLine'
+      );
+    }
 
-        expect(actualLine, equals(expectedLine),
-          reason: 'After $i steps, debugger should stop at $expectedLine but stopped at $actualLine');
-      }
-    });
-  }, timeout: const Timeout.factor(10), tags: <String>['integration']); // The DevFS sync takes a really long time, so these tests can be slow.
+    await _flutter.stop();
+    tryToDelete(tempDir);
+  });
 }
diff --git a/packages/flutter_tools/test/integration.shard/expression_evaluation_test.dart b/packages/flutter_tools/test/integration.shard/expression_evaluation_test.dart
index a15dad4..00038a8 100644
--- a/packages/flutter_tools/test/integration.shard/expression_evaluation_test.dart
+++ b/packages/flutter_tools/test/integration.shard/expression_evaluation_test.dart
@@ -2,12 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Integration tests which invoke flutter instead of unit testing the code
-// will not produce meaningful coverage information - we can measure coverage
-// from the isolate running the test, but not from the isolate started via
-// the command line process.
-@Tags(<String>['no_coverage'])
 import 'dart:async';
+import 'dart:io';
 
 import 'package:file/file.dart';
 import 'package:flutter_tools/src/base/file_system.dart';
@@ -20,120 +16,133 @@
 import 'test_driver.dart';
 import 'test_utils.dart';
 
-void main() {
-  group('flutter run expression evaluation', () {
-    Directory tempDir;
-    final BasicProject _project = BasicProject();
-    FlutterRunTestDriver _flutter;
+void batch1() {
+  final BasicProject _project = BasicProject();
+  Directory tempDir;
+  FlutterRunTestDriver _flutter;
 
-    setUp(() async {
-      tempDir = createResolvedTempDirectorySync('run_expression_eval_test.');
-      await _project.setUpIn(tempDir);
-      _flutter = FlutterRunTestDriver(tempDir);
-    });
+  Future<void> initProject() async {
+    tempDir = createResolvedTempDirectorySync('run_expression_eval_test.');
+    await _project.setUpIn(tempDir);
+    _flutter = FlutterRunTestDriver(tempDir);
+  }
 
-    tearDown(() async {
-      await _flutter.stop();
-      tryToDelete(tempDir);
-    });
+  Future<void> cleanProject() async {
+    await _flutter.stop();
+    tryToDelete(tempDir);
+  }
 
-    Future<void> breakInBuildMethod(FlutterTestDriver flutter) async {
-      await _flutter.breakAt(
-        _project.buildMethodBreakpointUri,
-        _project.buildMethodBreakpointLine,
-      );
-    }
+  Future<void> breakInBuildMethod(FlutterTestDriver flutter) async {
+    await _flutter.breakAt(
+      _project.buildMethodBreakpointUri,
+      _project.buildMethodBreakpointLine,
+    );
+  }
 
-    Future<void> breakInTopLevelFunction(FlutterTestDriver flutter) async {
-      await _flutter.breakAt(
-        _project.topLevelFunctionBreakpointUri,
-        _project.topLevelFunctionBreakpointLine,
-      );
-    }
+  Future<void> breakInTopLevelFunction(FlutterTestDriver flutter) async {
+    await _flutter.breakAt(
+      _project.topLevelFunctionBreakpointUri,
+      _project.topLevelFunctionBreakpointLine,
+    );
+  }
 
-    test('can evaluate trivial expressions in top level function', () async {
-      await _flutter.run(withDebugger: true);
-      await breakInTopLevelFunction(_flutter);
-      await evaluateTrivialExpressions(_flutter);
-    });
+  test('flutter run expression evaluation - can evaluate trivial expressions in top level function', () async {
+    await initProject();
+    await _flutter.run(withDebugger: true);
+    await breakInTopLevelFunction(_flutter);
+    await evaluateTrivialExpressions(_flutter);
+    await cleanProject();
+  });
 
-    test('can evaluate trivial expressions in build method', () async {
-      await _flutter.run(withDebugger: true);
-      await breakInBuildMethod(_flutter);
-      await evaluateTrivialExpressions(_flutter);
-    });
+  test('flutter run expression evaluation - can evaluate trivial expressions in build method', () async {
+    await initProject();
+    await _flutter.run(withDebugger: true);
+    await breakInBuildMethod(_flutter);
+    await evaluateTrivialExpressions(_flutter);
+    await cleanProject();
+  });
 
-    test('can evaluate complex expressions in top level function', () async {
-      await _flutter.run(withDebugger: true);
-      await breakInTopLevelFunction(_flutter);
-      await evaluateComplexExpressions(_flutter);
-    });
+  test('flutter run expression evaluation - can evaluate complex expressions in top level function', () async {
+    await initProject();
+    await _flutter.run(withDebugger: true);
+    await breakInTopLevelFunction(_flutter);
+    await evaluateComplexExpressions(_flutter);
+    await cleanProject();
+  });
 
-    test('can evaluate complex expressions in build method', () async {
-      await _flutter.run(withDebugger: true);
-      await breakInBuildMethod(_flutter);
-      await evaluateComplexExpressions(_flutter);
-    });
+  test('flutter run expression evaluation - can evaluate complex expressions in build method', () async {
+    await initProject();
+    await _flutter.run(withDebugger: true);
+    await breakInBuildMethod(_flutter);
+    await evaluateComplexExpressions(_flutter);
+    await cleanProject();
+  });
 
-    test('can evaluate expressions returning complex objects in top level function', () async {
-      await _flutter.run(withDebugger: true);
-      await breakInTopLevelFunction(_flutter);
-      await evaluateComplexReturningExpressions(_flutter);
-    });
+  test('flutter run expression evaluation - can evaluate expressions returning complex objects in top level function', () async {
+    await initProject();
+    await _flutter.run(withDebugger: true);
+    await breakInTopLevelFunction(_flutter);
+    await evaluateComplexReturningExpressions(_flutter);
+    await cleanProject();
+  });
 
-    test('can evaluate expressions returning complex objects in build method', () async {
-      await _flutter.run(withDebugger: true);
-      await breakInBuildMethod(_flutter);
-      await evaluateComplexReturningExpressions(_flutter);
-    });
-  }, timeout: const Timeout.factor(10), tags: <String>['integration']); // The DevFS sync takes a really long time, so these tests can be slow.
+  test('flutter run expression evaluation - can evaluate expressions returning complex objects in build method', () async {
+    await initProject();
+    await _flutter.run(withDebugger: true);
+    await breakInBuildMethod(_flutter);
+    await evaluateComplexReturningExpressions(_flutter);
+    await cleanProject();
+  });
+}
 
-  group('flutter test expression evaluation', () {
-    Directory tempDir;
-    final TestsProject _project = TestsProject();
-    FlutterTestTestDriver _flutter;
+void batch2() {
+  final TestsProject _project = TestsProject();
+  Directory tempDir;
+  FlutterTestTestDriver _flutter;
 
-    setUp(() async {
-      tempDir = createResolvedTempDirectorySync('test_expression_eval_test.');
-      await _project.setUpIn(tempDir);
-      _flutter = FlutterTestTestDriver(tempDir);
-    });
+  Future<void> initProject() async {
+    tempDir = createResolvedTempDirectorySync('test_expression_eval_test.');
+    await _project.setUpIn(tempDir);
+    _flutter = FlutterTestTestDriver(tempDir);
+  }
 
-    tearDown(() async {
-      await _flutter.quit();
-      tryToDelete(tempDir);
-    });
+  Future<void> cleanProject() async {
+    await _flutter?.quit();
+    tryToDelete(tempDir);
+  }
 
-    test('can evaluate trivial expressions in a test', () async {
-      await _flutter.test(
-        withDebugger: true,
-        beforeStart: () => _flutter.addBreakpoint(_project.breakpointUri, _project.breakpointLine),
-      );
-      await _flutter.waitForPause();
-      await evaluateTrivialExpressions(_flutter);
-      await _flutter.resume();
-    });
+  test('flutter test expression evaluation - can evaluate trivial expressions in a test', () async {
+    await initProject();
+    await _flutter.test(
+      withDebugger: true,
+      beforeStart: () => _flutter.addBreakpoint(_project.breakpointUri, _project.breakpointLine),
+    );
+    await _flutter.waitForPause();
+    await evaluateTrivialExpressions(_flutter);
+    await cleanProject();
+  });
 
-    test('can evaluate complex expressions in a test', () async {
-      await _flutter.test(
-        withDebugger: true,
-        beforeStart: () => _flutter.addBreakpoint(_project.breakpointUri, _project.breakpointLine),
-      );
-      await _flutter.waitForPause();
-      await evaluateComplexExpressions(_flutter);
-      await _flutter.resume();
-    });
+  test('flutter test expression evaluation - can evaluate complex expressions in a test', () async {
+    await initProject();
+    await _flutter.test(
+      withDebugger: true,
+      beforeStart: () => _flutter.addBreakpoint(_project.breakpointUri, _project.breakpointLine),
+    );
+    await _flutter.waitForPause();
+    await evaluateComplexExpressions(_flutter);
+    await cleanProject();
+  });
 
-    test('can evaluate expressions returning complex objects in a test', () async {
-      await _flutter.test(
-        withDebugger: true,
-        beforeStart: () => _flutter.addBreakpoint(_project.breakpointUri, _project.breakpointLine),
-      );
-      await _flutter.waitForPause();
-      await evaluateComplexReturningExpressions(_flutter);
-      await _flutter.resume();
-    });
-  }, timeout: const Timeout.factor(10), tags: <String>['integration']); // The DevFS sync takes a really long time, so these tests can be slow.
+  test('flutter test expression evaluation - can evaluate expressions returning complex objects in a test', () async {
+    await initProject();
+    await _flutter.test(
+      withDebugger: true,
+      beforeStart: () => _flutter.addBreakpoint(_project.breakpointUri, _project.breakpointLine),
+    );
+    await _flutter.waitForPause();
+    await evaluateComplexReturningExpressions(_flutter);
+    await cleanProject();
+  });
 }
 
 Future<void> evaluateTrivialExpressions(FlutterTestDriver flutter) async {
@@ -164,3 +173,8 @@
   final InstanceRef res = await flutter.evaluate(resp.id, r'"$year-$month-$day"');
   expect(res.valueAsString, equals('${now.year}-${now.month}-${now.day}'));
 }
+
+void main() {
+  batch1();
+  batch2();
+}
diff --git a/packages/flutter_tools/test/integration.shard/flutter_attach_test.dart b/packages/flutter_tools/test/integration.shard/flutter_attach_test.dart
index b5592c1..2d868b3 100644
--- a/packages/flutter_tools/test/integration.shard/flutter_attach_test.dart
+++ b/packages/flutter_tools/test/integration.shard/flutter_attach_test.dart
@@ -2,11 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Integration tests which invoke flutter instead of unit testing the code
-// will not produce meaningful coverage information - we can measure coverage
-// from the isolate running the test, but not from the isolate started via
-// the command line process.
-@Tags(<String>['no_coverage'])
+import 'dart:io';
+
 import 'package:file/file.dart';
 import 'package:flutter_tools/src/base/file_system.dart';
 
@@ -33,35 +30,36 @@
     tryToDelete(tempDir);
   });
 
-  group('attached process', () {
-    test('writes pid-file', () async {
-      final File pidFile = tempDir.childFile('test.pid');
-      await _flutterRun.run(withDebugger: true);
-      await _flutterAttach.attach(
-        _flutterRun.vmServicePort,
-        pidFile: pidFile,
-      );
-      expect(pidFile.existsSync(), isTrue);
-    });
-    test('can hot reload', () async {
-      await _flutterRun.run(withDebugger: true);
-      await _flutterAttach.attach(_flutterRun.vmServicePort);
-      await _flutterAttach.hotReload();
-    });
-    test('can detach, reattach, hot reload', () async {
-      await _flutterRun.run(withDebugger: true);
-      await _flutterAttach.attach(_flutterRun.vmServicePort);
-      await _flutterAttach.detach();
-      await _flutterAttach.attach(_flutterRun.vmServicePort);
-      await _flutterAttach.hotReload();
-    });
-    test('killing process behaves the same as detach ', () async {
-      await _flutterRun.run(withDebugger: true);
-      await _flutterAttach.attach(_flutterRun.vmServicePort);
-      await _flutterAttach.quit();
-      _flutterAttach = FlutterRunTestDriver(tempDir, logPrefix: 'ATTACH-2');
-      await _flutterAttach.attach(_flutterRun.vmServicePort);
-      await _flutterAttach.hotReload();
-    });
-  }, timeout: const Timeout.factor(10), tags: <String>['integration']); // The DevFS sync takes a really long time, so these tests can be slow.
+  test('writes pid-file', () async {
+    final File pidFile = tempDir.childFile('test.pid');
+    await _flutterRun.run(withDebugger: true);
+    await _flutterAttach.attach(
+      _flutterRun.vmServicePort,
+      pidFile: pidFile,
+    );
+    expect(pidFile.existsSync(), isTrue);
+  });
+
+  test('can hot reload', () async {
+    await _flutterRun.run(withDebugger: true);
+    await _flutterAttach.attach(_flutterRun.vmServicePort);
+    await _flutterAttach.hotReload();
+  });
+
+  test('can detach, reattach, hot reload', () async {
+    await _flutterRun.run(withDebugger: true);
+    await _flutterAttach.attach(_flutterRun.vmServicePort);
+    await _flutterAttach.detach();
+    await _flutterAttach.attach(_flutterRun.vmServicePort);
+    await _flutterAttach.hotReload();
+  });
+
+  test('killing process behaves the same as detach ', () async {
+    await _flutterRun.run(withDebugger: true);
+    await _flutterAttach.attach(_flutterRun.vmServicePort);
+    await _flutterAttach.quit();
+    _flutterAttach = FlutterRunTestDriver(tempDir, logPrefix: 'ATTACH-2');
+    await _flutterAttach.attach(_flutterRun.vmServicePort);
+    await _flutterAttach.hotReload();
+  });
 }
diff --git a/packages/flutter_tools/test/integration.shard/flutter_run_test.dart b/packages/flutter_tools/test/integration.shard/flutter_run_test.dart
index cc83323..32f50e1 100644
--- a/packages/flutter_tools/test/integration.shard/flutter_run_test.dart
+++ b/packages/flutter_tools/test/integration.shard/flutter_run_test.dart
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Integration tests which invoke flutter instead of unit testing the code
-// will not produce meaningful coverage information - we can measure coverage
-// from the isolate running the test, but not from the isolate started via
-// the command line process.
-@Tags(<String>['no_coverage'])
 import 'package:file/file.dart';
 import 'package:flutter_tools/src/base/file_system.dart';
 import 'package:flutter_tools/src/base/io.dart';
@@ -18,47 +13,45 @@
 import 'test_utils.dart';
 
 void main() {
-  group('flutter_run', () {
-    Directory tempDir;
-    final BasicProject _project = BasicProject();
-    FlutterRunTestDriver _flutter;
+  Directory tempDir;
+  final BasicProject _project = BasicProject();
+  FlutterRunTestDriver _flutter;
 
-    setUp(() async {
-      tempDir = createResolvedTempDirectorySync('run_test.');
-      await _project.setUpIn(tempDir);
-      _flutter = FlutterRunTestDriver(tempDir);
-    });
+  setUp(() async {
+    tempDir = createResolvedTempDirectorySync('run_test.');
+    await _project.setUpIn(tempDir);
+    _flutter = FlutterRunTestDriver(tempDir);
+  });
 
-    tearDown(() async {
-      await _flutter.stop();
-      tryToDelete(tempDir);
-    });
+  tearDown(() async {
+    await _flutter.stop();
+    tryToDelete(tempDir);
+  });
 
-    test('reports an error if an invalid device is supplied', () async {
-      // This test forces flutter to check for all possible devices to catch issues
-      // like https://github.com/flutter/flutter/issues/21418 which were skipped
-      // over because other integration tests run using flutter-tester which short-cuts
-      // some of the checks for devices.
-      final String flutterBin = fs.path.join(getFlutterRoot(), 'bin', 'flutter');
+  test('flutter run reports an error if an invalid device is supplied', () async {
+    // This test forces flutter to check for all possible devices to catch issues
+    // like https://github.com/flutter/flutter/issues/21418 which were skipped
+    // over because other integration tests run using flutter-tester which short-cuts
+    // some of the checks for devices.
+    final String flutterBin = fs.path.join(getFlutterRoot(), 'bin', 'flutter');
 
-      const ProcessManager _processManager = LocalProcessManager();
-      final ProcessResult _proc = await _processManager.run(
-        <String>[flutterBin, 'run', '-d', 'invalid-device-id'],
-        workingDirectory: tempDir.path,
-      );
+    const ProcessManager _processManager = LocalProcessManager();
+    final ProcessResult _proc = await _processManager.run(
+      <String>[flutterBin, 'run', '-d', 'invalid-device-id'],
+      workingDirectory: tempDir.path,
+    );
 
-      expect(_proc.stdout, isNot(contains('flutter has exited unexpectedly')));
-      expect(_proc.stderr, isNot(contains('flutter has exited unexpectedly')));
-      if (!_proc.stderr.toString().contains('Unable to locate a development')
-          && !_proc.stdout.toString().contains('No devices found with name or id matching')) {
-        fail("'flutter run -d invalid-device-id' did not produce the expected error");
-      }
-    });
+    expect(_proc.stdout, isNot(contains('flutter has exited unexpectedly')));
+    expect(_proc.stderr, isNot(contains('flutter has exited unexpectedly')));
+    if (!_proc.stderr.toString().contains('Unable to locate a development')
+        && !_proc.stdout.toString().contains('No devices found with name or id matching')) {
+      fail("'flutter run -d invalid-device-id' did not produce the expected error");
+    }
+  });
 
-    test('writes pid-file', () async {
-      final File pidFile = tempDir.childFile('test.pid');
-      await _flutter.run(pidFile: pidFile);
-      expect(pidFile.existsSync(), isTrue);
-    });
-  }, timeout: const Timeout.factor(10), tags: <String>['integration']); // The DevFS sync takes a really long time, so these tests can be slow.
+  test('flutter run writes pid-file', () async {
+    final File pidFile = tempDir.childFile('test.pid');
+    await _flutter.run(pidFile: pidFile);
+    expect(pidFile.existsSync(), isTrue);
+  });
 }
diff --git a/packages/flutter_tools/test/integration.shard/hot_reload_test.dart b/packages/flutter_tools/test/integration.shard/hot_reload_test.dart
index a651294..2d7d3cd 100644
--- a/packages/flutter_tools/test/integration.shard/hot_reload_test.dart
+++ b/packages/flutter_tools/test/integration.shard/hot_reload_test.dart
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Integration tests which invoke flutter instead of unit testing the code
-// will not produce meaningful coverage information - we can measure coverage
-// from the isolate running the test, but not from the isolate started via
-// the command line process.
-@Tags(<String>['no_coverage'])
 import 'dart:async';
 
 import 'package:file/file.dart';
@@ -20,139 +15,137 @@
 import 'test_utils.dart';
 
 void main() {
-  group('hot reload tests', () {
-    Directory tempDir;
-    final HotReloadProject _project = HotReloadProject();
-    FlutterRunTestDriver _flutter;
+  Directory tempDir;
+  final HotReloadProject _project = HotReloadProject();
+  FlutterRunTestDriver _flutter;
 
-    setUp(() async {
-      tempDir = createResolvedTempDirectorySync('hot_reload_test.');
-      await _project.setUpIn(tempDir);
-      _flutter = FlutterRunTestDriver(tempDir);
-    });
+  setUp(() async {
+    tempDir = createResolvedTempDirectorySync('hot_reload_test.');
+    await _project.setUpIn(tempDir);
+    _flutter = FlutterRunTestDriver(tempDir);
+  });
 
-    tearDown(() async {
-      await _flutter?.stop();
-      tryToDelete(tempDir);
-    });
+  tearDown(() async {
+    await _flutter?.stop();
+    tryToDelete(tempDir);
+  });
 
-    test('hot reload works without error', () async {
-      await _flutter.run();
+  test('hot reload works without error', () async {
+    await _flutter.run();
+    await _flutter.hotReload();
+  });
+
+  test('newly added code executes during hot reload', () async {
+    await _flutter.run();
+    _project.uncommentHotReloadPrint();
+    final StringBuffer stdout = StringBuffer();
+    final StreamSubscription<String> subscription = _flutter.stdout.listen(stdout.writeln);
+    try {
       await _flutter.hotReload();
-    });
-
-    test('newly added code executes during hot reload', () async {
-      await _flutter.run();
-      _project.uncommentHotReloadPrint();
-      final StringBuffer stdout = StringBuffer();
-      final StreamSubscription<String> subscription = _flutter.stdout.listen(stdout.writeln);
-      try {
-        await _flutter.hotReload();
-        expect(stdout.toString(), contains('(((((RELOAD WORKED)))))'));
-      } finally {
-        await subscription.cancel();
-      }
-    });
-
-    test('hot restart works without error', () async {
-      await _flutter.run();
-      await _flutter.hotRestart();
-    });
-
-    test('breakpoints are hit after hot reload', () async {
-      Isolate isolate;
-      await _flutter.run(withDebugger: true, startPaused: true);
-      final Completer<void> sawTick1 = Completer<void>();
-      final Completer<void> sawTick3 = Completer<void>();
-      final Completer<void> sawDebuggerPausedMessage = Completer<void>();
-      final StreamSubscription<String> subscription = _flutter.stdout.listen(
-        (String line) {
-          if (line.contains('((((TICK 1))))')) {
-            expect(sawTick1.isCompleted, isFalse);
-            sawTick1.complete();
-          }
-          if (line.contains('((((TICK 3))))')) {
-            expect(sawTick3.isCompleted, isFalse);
-            sawTick3.complete();
-          }
-          if (line.contains('The application is paused in the debugger on a breakpoint.')) {
-            expect(sawDebuggerPausedMessage.isCompleted, isFalse);
-            sawDebuggerPausedMessage.complete();
-          }
-        },
-      );
-      await _flutter.resume(); // we start paused so we can set up our TICK 1 listener before the app starts
-      unawaited(sawTick1.future.timeout(
-        const Duration(seconds: 5),
-        onTimeout: () { print('The test app is taking longer than expected to print its synchronization line...'); },
-      ));
-      await sawTick1.future; // after this, app is in steady state
-      await _flutter.addBreakpoint(
-        _project.scheduledBreakpointUri,
-        _project.scheduledBreakpointLine,
-      );
-      await _flutter.hotReload(); // reload triggers code which eventually hits the breakpoint
-      isolate = await _flutter.waitForPause();
-      expect(isolate.pauseEvent.kind, equals(EventKind.kPauseBreakpoint));
-      await _flutter.resume();
-      await _flutter.addBreakpoint(
-        _project.buildBreakpointUri,
-        _project.buildBreakpointLine,
-      );
-      bool reloaded = false;
-      final Future<void> reloadFuture = _flutter.hotReload().then((void value) { reloaded = true; });
-      await sawTick3.future; // this should happen before it pauses
-      isolate = await _flutter.waitForPause();
-      expect(isolate.pauseEvent.kind, equals(EventKind.kPauseBreakpoint));
-      await sawDebuggerPausedMessage.future;
-      expect(reloaded, isFalse);
-      await _flutter.resume();
-      await reloadFuture;
-      expect(reloaded, isTrue);
-      reloaded = false;
+      expect(stdout.toString(), contains('(((((RELOAD WORKED)))))'));
+    } finally {
       await subscription.cancel();
-    });
+    }
+  });
 
-    test('hot reload doesn\'t reassemble if paused', () async {
-      await _flutter.run(withDebugger: true);
-      final Completer<void> sawTick2 = Completer<void>();
-      final Completer<void> sawTick3 = Completer<void>();
-      final Completer<void> sawDebuggerPausedMessage1 = Completer<void>();
-      final Completer<void> sawDebuggerPausedMessage2 = Completer<void>();
-      final StreamSubscription<String> subscription = _flutter.stdout.listen(
-        (String line) {
-          if (line.contains('((((TICK 2))))')) {
-            expect(sawTick2.isCompleted, isFalse);
-            sawTick2.complete();
-          }
-          if (line.contains('The application is paused in the debugger on a breakpoint.')) {
-            expect(sawDebuggerPausedMessage1.isCompleted, isFalse);
-            sawDebuggerPausedMessage1.complete();
-          }
-          if (line.contains('The application is paused in the debugger on a breakpoint; interface might not update.')) {
-            expect(sawDebuggerPausedMessage2.isCompleted, isFalse);
-            sawDebuggerPausedMessage2.complete();
-          }
-        },
-      );
-      await _flutter.addBreakpoint(
-        _project.buildBreakpointUri,
-        _project.buildBreakpointLine,
-      );
-      bool reloaded = false;
-      final Future<void> reloadFuture = _flutter.hotReload().then((void value) { reloaded = true; });
-      await sawTick2.future; // this should happen before it pauses
-      final Isolate isolate = await _flutter.waitForPause();
-      expect(isolate.pauseEvent.kind, equals(EventKind.kPauseBreakpoint));
-      expect(reloaded, isFalse);
-      await sawDebuggerPausedMessage1.future; // this is the one where it say "uh, you broke into the debugger while reloading"
-      await reloadFuture; // this is the one where it times out because you're in the debugger
-      expect(reloaded, isTrue);
-      await _flutter.hotReload(); // now we're already paused
-      expect(sawTick3.isCompleted, isFalse);
-      await sawDebuggerPausedMessage2.future; // so we just get told that nothing is going to happen
-      await _flutter.resume();
-      await subscription.cancel();
-    });
-  }, timeout: const Timeout.factor(10), tags: <String>['integration']); // The DevFS sync takes a really long time, so these tests can be slow.
+  test('hot restart works without error', () async {
+    await _flutter.run();
+    await _flutter.hotRestart();
+  });
+
+  test('breakpoints are hit after hot reload', () async {
+    Isolate isolate;
+    await _flutter.run(withDebugger: true, startPaused: true);
+    final Completer<void> sawTick1 = Completer<void>();
+    final Completer<void> sawTick3 = Completer<void>();
+    final Completer<void> sawDebuggerPausedMessage = Completer<void>();
+    final StreamSubscription<String> subscription = _flutter.stdout.listen(
+      (String line) {
+        if (line.contains('((((TICK 1))))')) {
+          expect(sawTick1.isCompleted, isFalse);
+          sawTick1.complete();
+        }
+        if (line.contains('((((TICK 3))))')) {
+          expect(sawTick3.isCompleted, isFalse);
+          sawTick3.complete();
+        }
+        if (line.contains('The application is paused in the debugger on a breakpoint.')) {
+          expect(sawDebuggerPausedMessage.isCompleted, isFalse);
+          sawDebuggerPausedMessage.complete();
+        }
+      },
+    );
+    await _flutter.resume(); // we start paused so we can set up our TICK 1 listener before the app starts
+    unawaited(sawTick1.future.timeout(
+      const Duration(seconds: 5),
+      onTimeout: () { print('The test app is taking longer than expected to print its synchronization line...'); },
+    ));
+    await sawTick1.future; // after this, app is in steady state
+    await _flutter.addBreakpoint(
+      _project.scheduledBreakpointUri,
+      _project.scheduledBreakpointLine,
+    );
+    await _flutter.hotReload(); // reload triggers code which eventually hits the breakpoint
+    isolate = await _flutter.waitForPause();
+    expect(isolate.pauseEvent.kind, equals(EventKind.kPauseBreakpoint));
+    await _flutter.resume();
+    await _flutter.addBreakpoint(
+      _project.buildBreakpointUri,
+      _project.buildBreakpointLine,
+    );
+    bool reloaded = false;
+    final Future<void> reloadFuture = _flutter.hotReload().then((void value) { reloaded = true; });
+    await sawTick3.future; // this should happen before it pauses
+    isolate = await _flutter.waitForPause();
+    expect(isolate.pauseEvent.kind, equals(EventKind.kPauseBreakpoint));
+    await sawDebuggerPausedMessage.future;
+    expect(reloaded, isFalse);
+    await _flutter.resume();
+    await reloadFuture;
+    expect(reloaded, isTrue);
+    reloaded = false;
+    await subscription.cancel();
+  });
+
+  test('hot reload doesn\'t reassemble if paused', () async {
+    await _flutter.run(withDebugger: true);
+    final Completer<void> sawTick2 = Completer<void>();
+    final Completer<void> sawTick3 = Completer<void>();
+    final Completer<void> sawDebuggerPausedMessage1 = Completer<void>();
+    final Completer<void> sawDebuggerPausedMessage2 = Completer<void>();
+    final StreamSubscription<String> subscription = _flutter.stdout.listen(
+      (String line) {
+        if (line.contains('((((TICK 2))))')) {
+          expect(sawTick2.isCompleted, isFalse);
+          sawTick2.complete();
+        }
+        if (line.contains('The application is paused in the debugger on a breakpoint.')) {
+          expect(sawDebuggerPausedMessage1.isCompleted, isFalse);
+          sawDebuggerPausedMessage1.complete();
+        }
+        if (line.contains('The application is paused in the debugger on a breakpoint; interface might not update.')) {
+          expect(sawDebuggerPausedMessage2.isCompleted, isFalse);
+          sawDebuggerPausedMessage2.complete();
+        }
+      },
+    );
+    await _flutter.addBreakpoint(
+      _project.buildBreakpointUri,
+      _project.buildBreakpointLine,
+    );
+    bool reloaded = false;
+    final Future<void> reloadFuture = _flutter.hotReload().then((void value) { reloaded = true; });
+    await sawTick2.future; // this should happen before it pauses
+    final Isolate isolate = await _flutter.waitForPause();
+    expect(isolate.pauseEvent.kind, equals(EventKind.kPauseBreakpoint));
+    expect(reloaded, isFalse);
+    await sawDebuggerPausedMessage1.future; // this is the one where it say "uh, you broke into the debugger while reloading"
+    await reloadFuture; // this is the one where it times out because you're in the debugger
+    expect(reloaded, isTrue);
+    await _flutter.hotReload(); // now we're already paused
+    expect(sawTick3.isCompleted, isFalse);
+    await sawDebuggerPausedMessage2.future; // so we just get told that nothing is going to happen
+    await _flutter.resume();
+    await subscription.cancel();
+  });
 }
diff --git a/packages/flutter_tools/test/integration.shard/lifetime_test.dart b/packages/flutter_tools/test/integration.shard/lifetime_test.dart
index 9e5284a..1ff3152 100644
--- a/packages/flutter_tools/test/integration.shard/lifetime_test.dart
+++ b/packages/flutter_tools/test/integration.shard/lifetime_test.dart
@@ -2,11 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// Integration tests which invoke flutter instead of unit testing the code
-// will not produce meaningful coverage information - we can measure coverage
-// from the isolate running the test, but not from the isolate started via
-// the command line process.
-@Tags(<String>['no_coverage'])
 import 'dart:async';
 
 import 'package:file/file.dart';
@@ -23,32 +18,30 @@
 const Duration requiredLifespan = Duration(seconds: 5);
 
 void main() {
-  group('flutter run', () {
-    final BasicProject _project = BasicProject();
-    FlutterRunTestDriver _flutter;
-    Directory tempDir;
+  final BasicProject _project = BasicProject();
+  FlutterRunTestDriver _flutter;
+  Directory tempDir;
 
-    setUp(() async {
-      tempDir = createResolvedTempDirectorySync('lifetime_test.');
-      await _project.setUpIn(tempDir);
-      _flutter = FlutterRunTestDriver(tempDir);
-    });
+  setUp(() async {
+    tempDir = createResolvedTempDirectorySync('lifetime_test.');
+    await _project.setUpIn(tempDir);
+    _flutter = FlutterRunTestDriver(tempDir);
+  });
 
-    tearDown(() async {
-      await _flutter.stop();
-      tryToDelete(tempDir);
-    });
+  tearDown(() async {
+    await _flutter.stop();
+    tryToDelete(tempDir);
+  });
 
-    test('does not terminate when a debugger is attached', () async {
-      await _flutter.run(withDebugger: true);
-      await Future<void>.delayed(requiredLifespan);
-      expect(_flutter.hasExited, equals(false));
-    });
+  test('flutter run does not terminate when a debugger is attached', () async {
+    await _flutter.run(withDebugger: true);
+    await Future<void>.delayed(requiredLifespan);
+    expect(_flutter.hasExited, equals(false));
+  });
 
-    test('does not terminate when a debugger is attached and pause-on-exceptions', () async {
-      await _flutter.run(withDebugger: true, pauseOnExceptions: true);
-      await Future<void>.delayed(requiredLifespan);
-      expect(_flutter.hasExited, equals(false));
-    });
-  }, timeout: const Timeout.factor(10), tags: <String>['integration']); // The DevFS sync takes a really long time, so these tests can be slow.
+  test('fluter run does not terminate when a debugger is attached and pause-on-exceptions', () async {
+    await _flutter.run(withDebugger: true, pauseOnExceptions: true);
+    await Future<void>.delayed(requiredLifespan);
+    expect(_flutter.hasExited, equals(false));
+  });
 }
diff --git a/packages/flutter_tools/test/src/common.dart b/packages/flutter_tools/test/src/common.dart
index 7741daf..00de957 100644
--- a/packages/flutter_tools/test/src/common.dart
+++ b/packages/flutter_tools/test/src/common.dart
@@ -127,13 +127,6 @@
   return projectPath;
 }
 
-/// Test case timeout for tests involving remote calls to `pub get` or similar.
-const Timeout allowForRemotePubInvocation = Timeout.factor(10.0);
-
-/// Test case timeout for tests involving creating a Flutter project with
-/// `--no-pub`. Use [allowForRemotePubInvocation] when creation involves `pub`.
-const Timeout allowForCreateFlutterProject = Timeout.factor(3.0);
-
 Future<void> expectToolExitLater(Future<dynamic> future, Matcher messageMatcher) async {
   try {
     await future;
diff --git a/packages/flutter_tools/test/src/context.dart b/packages/flutter_tools/test/src/context.dart
index 57e4465..7f1fdff 100644
--- a/packages/flutter_tools/test/src/context.dart
+++ b/packages/flutter_tools/test/src/context.dart
@@ -48,7 +48,6 @@
 void testUsingContext(
   String description,
   dynamic testMethod(), {
-  Timeout timeout,
   Map<Type, Generator> overrides = const <Type, Generator>{},
   bool initializeFlutterRoot = true,
   String testOn,
@@ -137,8 +136,7 @@
         },
       );
     });
-  }, timeout: timeout ?? const Timeout(Duration(seconds: 60)),
-      testOn: testOn, skip: skip);
+  }, testOn: testOn, skip: skip);
 }
 
 void _printBufferedErrors(AppContext testContext) {