Add coverage recipe.

This recipe will be used for builds running as low priority and not
blocking the build. This is going to be a very expensive build running
from 90 minutes.

Bug: https://github.com/flutter/flutter/issues/81803
Change-Id: Ic4e6f9c99fd154fee8024e96d6ce07d74b15f126
Reviewed-on: https://flutter-review.googlesource.com/c/recipes/+/34740
Reviewed-by: Ricardo Amador <ricardoamador@google.com>
Commit-Queue: Godofredo Contreras <godofredoc@google.com>
diff --git a/recipes/flutter/coverage.expected/coverage.json b/recipes/flutter/coverage.expected/coverage.json
new file mode 100644
index 0000000..d2cf01c
--- /dev/null
+++ b/recipes/flutter/coverage.expected/coverage.json
@@ -0,0 +1,259 @@
+[
+  {
+    "cmd": [],
+    "name": "checkout source code"
+  },
+  {
+    "cmd": [],
+    "name": "checkout source code.Checkout flutter/flutter",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "python3",
+      "-u",
+      "RECIPE_MODULE[depot_tools::git]/resources/git_setup.py",
+      "--path",
+      "[START_DIR]/flutter sdk",
+      "--url",
+      "https://flutter.googlesource.com/mirrors/flutter"
+    ],
+    "name": "checkout source code.Checkout flutter/flutter.git setup",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "fetch",
+      "origin",
+      "master",
+      "--recurse-submodules",
+      "--progress",
+      "--tags"
+    ],
+    "cwd": "[START_DIR]/flutter sdk",
+    "env": {
+      "PATH": "RECIPE_REPO[depot_tools]:<PATH>"
+    },
+    "infra_step": true,
+    "name": "checkout source code.Checkout flutter/flutter.git fetch",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "checkout",
+      "-f",
+      "FETCH_HEAD"
+    ],
+    "cwd": "[START_DIR]/flutter sdk",
+    "infra_step": true,
+    "name": "checkout source code.Checkout flutter/flutter.git checkout",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/flutter sdk",
+    "infra_step": true,
+    "name": "checkout source code.Checkout flutter/flutter.read revision",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@",
+      "@@@STEP_TEXT@<br/>checked out 'deadbeef'<br/>@@@",
+      "@@@SET_BUILD_PROPERTY@got_revision@\"deadbeef\"@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "clean",
+      "-f",
+      "-d",
+      "-x"
+    ],
+    "cwd": "[START_DIR]/flutter sdk",
+    "infra_step": true,
+    "name": "checkout source code.Checkout flutter/flutter.git clean",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "sync"
+    ],
+    "cwd": "[START_DIR]/flutter sdk",
+    "infra_step": true,
+    "name": "checkout source code.Checkout flutter/flutter.submodule sync",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "submodule",
+      "update",
+      "--init",
+      "--recursive"
+    ],
+    "cwd": "[START_DIR]/flutter sdk",
+    "infra_step": true,
+    "name": "checkout source code.Checkout flutter/flutter.submodule update",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@2@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "git",
+      "rev-parse",
+      "HEAD"
+    ],
+    "cwd": "[START_DIR]/flutter sdk",
+    "infra_step": true,
+    "name": "git rev-parse"
+  },
+  {
+    "cmd": [],
+    "name": "prepare environment"
+  },
+  {
+    "cmd": [
+      "flutter",
+      "doctor"
+    ],
+    "cwd": "[START_DIR]/flutter sdk/packages/flutter",
+    "env": {
+      "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
+      "GIT_BRANCH": "",
+      "LUCI_BRANCH": "",
+      "LUCI_CI": "True",
+      "LUCI_PR": "",
+      "OS": "linux",
+      "PUB_CACHE": "[START_DIR]/.pub-cache",
+      "REVISION": "",
+      "SDK_CHECKOUT_PATH": "[START_DIR]/flutter sdk"
+    },
+    "env_prefixes": {
+      "PATH": [
+        "[START_DIR]/flutter sdk/bin",
+        "[START_DIR]/flutter sdk/bin/cache/dart-sdk/bin"
+      ]
+    },
+    "name": "prepare environment.flutter doctor",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "flutter",
+      "update-packages"
+    ],
+    "cwd": "[START_DIR]/flutter sdk/packages/flutter",
+    "env": {
+      "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
+      "GIT_BRANCH": "",
+      "LUCI_BRANCH": "",
+      "LUCI_CI": "True",
+      "LUCI_PR": "",
+      "OS": "linux",
+      "PUB_CACHE": "[START_DIR]/.pub-cache",
+      "REVISION": "",
+      "SDK_CHECKOUT_PATH": "[START_DIR]/flutter sdk"
+    },
+    "env_prefixes": {
+      "PATH": [
+        "[START_DIR]/flutter sdk/bin",
+        "[START_DIR]/flutter sdk/bin/cache/dart-sdk/bin"
+      ]
+    },
+    "infra_step": true,
+    "name": "prepare environment.download dependencies",
+    "~followup_annotations": [
+      "@@@STEP_NEST_LEVEL@1@@@"
+    ]
+  },
+  {
+    "cmd": [
+      "flutter",
+      "test",
+      "--coverage",
+      "-j",
+      "1"
+    ],
+    "cwd": "[START_DIR]/flutter sdk/packages/flutter",
+    "env": {
+      "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
+      "GIT_BRANCH": "",
+      "LUCI_BRANCH": "",
+      "LUCI_CI": "True",
+      "LUCI_PR": "",
+      "OS": "linux",
+      "PUB_CACHE": "[START_DIR]/.pub-cache",
+      "REVISION": "",
+      "SDK_CHECKOUT_PATH": "[START_DIR]/flutter sdk"
+    },
+    "env_prefixes": {
+      "PATH": [
+        "[START_DIR]/flutter sdk/bin",
+        "[START_DIR]/flutter sdk/bin/cache/dart-sdk/bin"
+      ]
+    },
+    "name": "flutter coverage"
+  },
+  {
+    "cmd": [
+      "python3",
+      "-u",
+      "RECIPE_MODULE[depot_tools::gsutil]/resources/gsutil_smart_retry.py",
+      "--",
+      "RECIPE_REPO[depot_tools]/gsutil.py",
+      "-m",
+      "----",
+      "cp",
+      "[START_DIR]/flutter sdk/packages/flutter/coverage/lcov.info",
+      "gs://flutter_infra_release/flutter/coverage/lcov.info"
+    ],
+    "cwd": "[START_DIR]/flutter sdk/packages/flutter",
+    "env": {
+      "DEPOT_TOOLS": "RECIPE_REPO[depot_tools]",
+      "GIT_BRANCH": "",
+      "LUCI_BRANCH": "",
+      "LUCI_CI": "True",
+      "LUCI_PR": "",
+      "OS": "linux",
+      "PUB_CACHE": "[START_DIR]/.pub-cache",
+      "REVISION": "",
+      "SDK_CHECKOUT_PATH": "[START_DIR]/flutter sdk"
+    },
+    "env_prefixes": {
+      "PATH": [
+        "[START_DIR]/flutter sdk/bin",
+        "[START_DIR]/flutter sdk/bin/cache/dart-sdk/bin"
+      ]
+    },
+    "infra_step": true,
+    "name": "gsutil upload lcov.info",
+    "~followup_annotations": [
+      "@@@STEP_LINK@lcov.info@https://storage.googleapis.com/flutter_infra_release/flutter/coverage/lcov.info@@@"
+    ]
+  },
+  {
+    "name": "$result"
+  }
+]
\ No newline at end of file
diff --git a/recipes/flutter/coverage.py b/recipes/flutter/coverage.py
new file mode 100644
index 0000000..6be5da4
--- /dev/null
+++ b/recipes/flutter/coverage.py
@@ -0,0 +1,65 @@
+# Copyright 2022 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.
+
+DEPS = [
+    'depot_tools/gsutil',
+    'flutter/flutter_deps',
+    'flutter/repo_util',
+    'recipe_engine/context',
+    'recipe_engine/path',
+    'recipe_engine/properties',
+    'recipe_engine/step',
+]
+
+
+def RunSteps(api):
+  """Recipe to collect coverage used by the flutter tool."""
+  checkout_path = api.path['start_dir'].join('flutter sdk')
+  with api.step.nest('checkout source code'):
+    api.repo_util.checkout(
+        'flutter',
+        checkout_path=checkout_path,
+        url=api.properties.get('git_url'),
+        ref=api.properties.get('git_ref')
+    )
+
+  env, env_prefixes = api.repo_util.flutter_environment(checkout_path)
+  api.flutter_deps.required_deps(
+      env, env_prefixes, api.properties.get('dependencies', [])
+  )
+
+  packages_path = checkout_path.join('packages', 'flutter')
+  with api.context(env=env, env_prefixes=env_prefixes, cwd=packages_path):
+    with api.step.nest('prepare environment'), api.step.defer_results():
+      api.step(
+          'flutter doctor',
+          ['flutter', 'doctor'],
+      )
+      api.step(
+          'download dependencies',
+          ['flutter', 'update-packages'],
+          infra_step=True,
+      )
+
+    api.step(
+        'flutter coverage',
+        ['flutter', 'test', '--coverage', '-j', '1'],
+    )
+    lcov_path = packages_path.join('coverage', 'lcov.info')
+    api.gsutil.upload(
+          bucket='flutter_infra_release',
+          source=lcov_path,
+          dest='flutter/coverage/lcov.info',
+          link_name='lcov.info',
+          multithreaded=True,
+          name='upload lcov.info',
+          unauthenticated_url=True
+      )
+
+
+def GenTests(api):
+  yield api.test(
+      'coverage',
+      api.repo_util.flutter_environment_data()
+  )