Automatic compilation
diff --git a/.ci.yaml b/.ci.yaml
deleted file mode 100644
index ca2ee9f..0000000
--- a/.ci.yaml
+++ /dev/null
@@ -1,162 +0,0 @@
-# Describes the targets run in continuous integration environment.
-#
-# Flutter infra uses this file to generate a checklist of tasks to be performed
-# for every commit.
-#
-# More information at:
-# * https://github.com/flutter/cocoon/blob/main/CI_YAML.md
-enabled_branches:
- - main
-
-platform_properties:
- linux:
- properties:
- os: Linux
- device_type: "none"
- mac:
- properties:
- os: "Mac-12|Mac-13"
- cpu: x86
- mac_arm64:
- properties:
- os: "Mac-12|Mac-13"
- cpu: arm64
- windows:
- properties:
- os: Windows
- device_type: "none"
-
-targets:
- - name: Linux Cocoon
- recipe: cocoon/cocoon
- properties:
- add_recipes_cq: "true"
- runIf:
- - .ci.yaml
- - analyze/**
- - app_dart/**
- - auto_submit/**
- - cipd_packages/**
- - cloud_build/**
- - dashboard/**
- - dev/**
- - licenses/**
- - packages/**
- - test_utilities/**
- - tooling/**
-
- - name: Linux device_doctor
- recipe: cocoon/cipd
- properties:
- script: cipd_packages/device_doctor/tool/build.sh
- cipd_name: flutter/device_doctor/linux-amd64
- runIf:
- - cipd_packages/device_doctor/**
- - .ci.yaml
-
- - name: Mac device_doctor
- recipe: cocoon/cipd
- properties:
- script: cipd_packages/device_doctor/tool/build.sh
- cipd_name: flutter/device_doctor/mac-amd64
- device_type: none
- runIf:
- - cipd_packages/device_doctor/**
- - .ci.yaml
-
- - name: Mac_arm64 device_doctor
- recipe: cocoon/cipd
- properties:
- script: cipd_packages/device_doctor/tool/build.sh
- cipd_name: flutter/device_doctor/mac-arm64
- device_type: none
- runIf:
- - cipd_packages/device_doctor/**
- - .ci.yaml
-
- - name: Windows device_doctor
- recipe: cocoon/cipd
- properties:
- script: cipd_packages\device_doctor\tool\build.bat
- cipd_name: flutter/device_doctor/windows-amd64
- runIf:
- - cipd_packages/device_doctor/**
- - .ci.yaml
-
- - name: Linux doxygen
- recipe: cocoon/cipd
- properties:
- script: cipd_packages/doxygen/tool/build.sh
- cipd_name: flutter/doxygen/linux-amd64
- dependencies: >-
- [
- {"dependency": "cmake", "version": "build_id:8787856497187628321"}
- ]
- runIf:
- - cipd_packages/doxygen/**
- - .ci.yaml
-
- - name: Mac codesign
- recipe: cocoon/cipd
- properties:
- script: cipd_packages/codesign/tool/build.sh
- cipd_name: flutter/codesign/mac-amd64
- device_type: none
- runIf:
- - cipd_packages/codesign/**
- - .ci.yaml
-
- - name: Mac_arm64 codesign
- recipe: cocoon/cipd
- properties:
- script: cipd_packages/codesign/tool/build.sh
- cipd_name: flutter/codesign/mac-arm64
- device_type: none
- runIf:
- - cipd_packages/codesign/**
- - .ci.yaml
-
- - name: Mac ruby
- recipe: cocoon/cipd
- timeout: 60
- properties:
- script: cipd_packages/ruby/tools/build.sh
- cipd_name: flutter/ruby/mac-amd64
- device_os: iOS
- contexts: >-
- [
- "osx_sdk_devicelab"
- ]
- $flutter/osx_sdk : >-
- {
- "sdk_version": "14e300c"
- }
- runIf:
- - cipd_packages/ruby/**
- - .ci.yaml
-
- - name: Mac_arm64 ruby
- recipe: cocoon/cipd
- timeout: 60
- properties:
- script: cipd_packages/ruby/tools/build.sh
- cipd_name: flutter/ruby/mac-arm64
- device_os: iOS
- contexts: >-
- [
- "osx_sdk_devicelab"
- ]
- $flutter/osx_sdk : >-
- {
- "sdk_version": "14e300c"
- }
- runIf:
- - cipd_packages/ruby/**
- - .ci.yaml
-
- - name: Linux ci_yaml roller
- recipe: infra/ci_yaml
- properties:
- backfill: "false"
- runIf:
- - .ci.yaml
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
deleted file mode 100644
index 9642af8..0000000
--- a/.github/dependabot.yml
+++ /dev/null
@@ -1,89 +0,0 @@
-# See Dependabot documentation for all configuration options:
-# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
-
-version: 2
-enable-beta-ecosystems: true
-updates:
- # Github actions ecosystem.
- - package-ecosystem: "github-actions"
- directory: "/"
- schedule:
- interval: "daily"
- labels:
- - "autosubmit"
- # Pub ecosystem.
- - package-ecosystem: "pub"
- directory: "/analyze"
- schedule:
- interval: "daily"
- labels:
- - "autosubmit"
- - package-ecosystem: "pub"
- directory: "/app_dart"
- schedule:
- interval: "daily"
- labels:
- - "autosubmit"
- - package-ecosystem: "pub"
- directory: "/auto_submit"
- schedule:
- interval: "daily"
- labels:
- - "autosubmit"
- - package-ecosystem: "pub"
- directory: "/cipd_packages/codesign"
- schedule:
- interval: "daily"
- labels:
- - "autosubmit"
- - package-ecosystem: "pub"
- directory: "/dashboard"
- schedule:
- interval: "daily"
- labels:
- - "autosubmit"
- - package-ecosystem: "pub"
- directory: "/cipd_packages/device_doctor"
- schedule:
- interval: "daily"
- labels:
- - "autosubmit"
- - package-ecosystem: "pub"
- directory: "/test_utilities"
- schedule:
- interval: "daily"
- labels:
- - "autosubmit"
- - package-ecosystem: "pub"
- directory: "/licenses"
- schedule:
- interval: "daily"
- labels:
- - "autosubmit"
- # Docker ecosystem.
- - package-ecosystem: "docker"
- directory: "/app_dart"
- schedule:
- interval: "daily"
- labels:
- - "autosubmit"
- - package-ecosystem: "docker"
- directory: "/auto_submit"
- schedule:
- interval: "daily"
- labels:
- - "autosubmit"
- # Go ecosystem.
- - package-ecosystem: "gomod"
- directory: "/tooling"
- schedule:
- interval: "daily"
- labels:
- - "autosubmit"
- allow:
- - dependency-name: "github.com/slsa-framework/slsa-verifier/v2"
- # Npm ecosystem.
- - package-ecosystem: 'npm'
- directory: '/gh_actions/third_party/no-response'
- schedule:
- interval: 'daily'
diff --git a/.github/workflows/no-response_publish.yaml b/.github/workflows/no-response_publish.yaml
deleted file mode 100644
index fc3d0b4..0000000
--- a/.github/workflows/no-response_publish.yaml
+++ /dev/null
@@ -1,40 +0,0 @@
-name: no-response-publish
-
-# Declare default permissions as read only.
-permissions: read-all
-
-on:
- release:
- types: [published, edited]
- branches:
- - main
- paths:
- - 'gh_actions/third_party/no-response/**'
- - '.github/workflows/no-response_test.yaml'
- - '.github/workflows/no-response_publish.yaml'
-jobs:
- build:
- runs-on: ubuntu-latest
- permissions:
- contents: write
- if: ${{ github.repository == 'flutter/cocoon' }}
- steps:
- - name: Checkout
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- with:
- ref: ${{ github.event.release.tag_name }}
- sparse-checkout: 'gh_actions/third_party/no-response'
- sparse-checkout-cone-mode: false
- - name: move_package_to_root
- run: |
- mv -f gh_actions/third_party/no-response/{.[!.],}* ./
- rm -rf gh_actions
- - name: ls
- run: ls -la
- - name: npm_ci
- run: npm ci
- - name: npm_run_build
- run: npm run build
- - uses: JasonEtco/build-and-tag-action@dd5e4991048c325f6d85b4155e586fc211c644da
- env:
- GITHUB_TOKEN: ${{ secrets.FLUTTERGITHUBBOT_TOKEN }}
diff --git a/.github/workflows/no-response_test.yaml b/.github/workflows/no-response_test.yaml
deleted file mode 100644
index f82627e..0000000
--- a/.github/workflows/no-response_test.yaml
+++ /dev/null
@@ -1,34 +0,0 @@
-name: no-response-test
-
-# Declare default permissions as read only.
-permissions: read-all
-
-on:
- pull_request:
- paths:
- - 'gh_actions/third_party/no-response/**'
- - '.github/workflows/no-response_test.yaml'
- - '.github/workflows/no-response_publish.yaml'
-jobs:
- unitTest:
- runs-on: ubuntu-latest
- if: ${{ github.repository == 'flutter/cocoon' }}
- steps:
- - name: Checkout
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- with:
- ref: ${{ github.event.release.tag_name }}
- sparse-checkout: 'gh_actions/third_party/no-response'
- sparse-checkout-cone-mode: false
- - name: move_package_to_root
- run: |
- mv -f gh_actions/third_party/no-response/{.[!.],}* ./
- rm -rf gh_actions
- - name: ls
- run: ls -la
- - name: npm_ci
- run: npm ci
- - name: npm_run_ci
- run: npm run ci
- - name: npm_run_build
- run: npm run build
diff --git a/.github/workflows/scorecards-analysis.yml b/.github/workflows/scorecards-analysis.yml
deleted file mode 100644
index 1cc1c8f..0000000
--- a/.github/workflows/scorecards-analysis.yml
+++ /dev/null
@@ -1,56 +0,0 @@
-name: Scorecards supply-chain security
-on:
- # Only the default branch is supported.
- branch_protection_rule:
- push:
- branches: [ main ]
-
-# Declare default permissions as read only.
-permissions: read-all
-
-jobs:
- analysis:
- name: Scorecards analysis
- runs-on: ubuntu-latest
- if: ${{ github.repository == 'flutter/cocoon' }}
- permissions:
- # Needed to upload the results to code-scanning dashboard.
- security-events: write
- actions: read
- contents: read
- # Needed to access OIDC token.
- id-token: write
-
- steps:
- - name: "Checkout code"
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11
- with:
- persist-credentials: false
-
- - name: "Run analysis"
- uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736
- with:
- results_file: results.sarif
- results_format: sarif
- # Read-only PAT token. To create it,
- # follow the steps in https://github.com/ossf/scorecard-action#pat-token-creation.
- repo_token: ${{ secrets.SCORECARD_READ_TOKEN }}
- # Publish the results to enable scorecard badges. For more details, see
- # https://github.com/ossf/scorecard-action#publishing-results.
- # For private repositories, `publish_results` will automatically be set to `false`,
- # regardless of the value entered here.
- publish_results: true
-
- # Upload the results as artifacts (optional).
- - name: "Upload artifact"
- uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32
- with:
- name: SARIF file
- path: results.sarif
- retention-days: 5
-
- # Upload the results to GitHub's code scanning dashboard.
- - name: "Upload to code-scanning"
- uses: github/codeql-action/upload-sarif@cdcdbb579706841c47f7063dda365e292e5cad7a
- with:
- sarif_file: results.sarif
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 2466d2f..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,13 +0,0 @@
-.dart_tool
-.DS_Store
-*~
-.packages
-.pub
-**/pubspec.lock
-.flutter-plugins-dependencies
-# IntelliJ
-.idea
-*.iml
-
-# VSCode
-.vscode/
diff --git a/AUTHORS b/AUTHORS
deleted file mode 100644
index 5a547b7..0000000
--- a/AUTHORS
+++ /dev/null
@@ -1,7 +0,0 @@
-# Below is a list of people and organizations that have contributed
-# to the Sky project. Names should be added to the list like so:
-#
-# Name/Organization <email address>
-
-Google Inc.
-Chromium authors.
diff --git a/CI_YAML.md b/CI_YAML.md
deleted file mode 100644
index a8efb1e..0000000
--- a/CI_YAML.md
+++ /dev/null
@@ -1,310 +0,0 @@
-# Cocoon Scheduler
-
-This Dart project contains logic for constructing infrastructure configs
-to validate commits in the repositories owned by Flutter.
-
-## ci.yaml
-
-This is the config file in a repository used to tell Cocoon what tasks are used
-to validate commits. It includes both the tasks used in presubmit and postsubmit.
-
-In addition, it supports tasks from different infrastructures as long as cocoon
-supports that `scheduler`. Only `luci` and `cocoon` are supported, but contributions
-are welcome.
-
-Example config:
-```yaml
-# /.ci.yaml
-
-# Enabled branches is a list of regexes, with the assumption that these are full line matches.
-# Internally, Cocoon prefixes these with $ and suffixes with ^ to enable matches.
-enabled_branches:
- - main
- - flutter-\\d+\\.\\d+-candidate\\.\\d+
-
-# Platform properties defines common properties shared among targets from the same platform.
-platform_properties:
- linux:
- properties:
- # os will be inherited by all Linux targets, but it can be overrided at the target level
- os: Linux
-
-targets:
-# A Target is an individual unit of work that is scheduled by Flutter infra
-# Target's are composed of the following properties:
-# name: A human readable string to uniquely identify this target.
-# The first word indicates the platform this test will be run on. This should match
-# to an existing platform under platform_properties.
-# recipes: LUCI recipes the target follows to run tests
-# https://flutter.googlesource.com/recipes/+/refs/heads/main/recipes/
-# bringup: Whether this target is under active development and should not block the tree.
-# If true, will not run in presubmit and will not block postsubmit.
-# presubmit: Whether to run this target on presubmit (defaults to true).
-# postsubmit: Whether to run this target on postsubmit (defaults to true).
-# run_if: List of path regexes that can trigger this target on presubmit.
-# If none are passed, it will evaluare run_if_not. If both are empty the target
-# will always run in presubmit.
-# run_if_not: List of path regexes used to filter out presubmit targets. The target will
-# be run only if the files changed do not match any paths in this list. If run_if
-# is provided and not empty run_if_not will be ignored.
-# enabled_branches: List of strings of branches this target can run on.
-# This overrides the global enabled_branches.
-# properties: A map of string, string. Values are parsed to their closest data model.
-# postsubmit_properties: Properties that are only run on postsubmit.
-# timeout: Integer defining whole build execution time limit for all steps in minutes.
-#
-# Minimal example:
-# Linux analyze will run on all presubmit and in postsubmit.
- - name: Linux analyze
-#
-# Bringup example:
-# Linux licenses will run on postsubmit, but it also passes the properties
-# `analyze=true` to the builder. Since `bringup=true`, presubmit is not run,
-# and postsubmit runs will not block the tree.
- - name: Linux licenses
- bringup: true
- properties:
- - analyze: license
-
-#
-# Tags example:
-# This test will be categorized as host only framework test.
-# Postsubmit runs will be passed "upload_metrics: true".
- - name: Linux analyze
- properties:
- tags: >-
- ["framework", "hostonly"]
- postsubmit_properties:
- - upload_metrics: "true"
-
-#
-# Devicelab example:
-# For tests that are located https://github.com/flutter/flutter/tree/master/dev/devicelab/bin/tasks:
-# 1) target name follows format of `<platform> <taskname>`
-# 2) properties
-# 2.1) update `tags` based on hosts, devices, and tests type. These tags will be used for statistic analysis.
-# 2.2) a `taskname` property is required, which should match the task name
-#
-# Here is the target config for a task named: `analyzer_benchmark.dart`.
- - name: Linux_android analyzer_benchmark
- recipe: devicelab/devicelab_drone
- presubmit: false
- properties:
- tags: >
- ["devicelab", "android", "linux"]
- task_name: analyzer_benchmark
-```
-
-### Adding new targets
-
-All new targets should be added as `bringup: true` to ensure they do not block the tree.
-
-Targets first need to be mirrored to flutter/infra before they will be run.
-This propagation takes about 30 minutes, and will only run as non-blocking in postsubmit.
-
-The target will show runs in https://ci.chromium.org/p/flutter (under the repo). See
-https://github.com/flutter/flutter/wiki/Adding-a-new-Test-Shard for up to date information
-on the steps to promote your target to blocking.
-
-For flutter/flutter, there's a GitHub bot that will
-promote a test that has been passing for the past 50 runs.
-
-### Test Ownership
-
-**This only applies to flutter/flutter**
-
-To prevent tests from rotting, all targets are required to have a clear owner. Add an
-owner in [TESTOWNERS](https://github.com/flutter/flutter/blob/master/TESTOWNERS)
-
-### Properties
-
-Targets support specifying properties that can be passed throughout infrastructure. The
-following are a list of keys that are reserved for special use.
-
-**Properties is a Map<String, String> and any special values must be JSON encoded
-(i.e. no trailing commas). Additionally, these strings must be compatible with YAML multiline strings**
-
-**$flutter/osx_sdk**: xcode configs including sdk and runtime. **Note**: support on legacy `xcode`/`runtime`
-properties and `xcode` dependency has been deprecated.
-
-Example:
-``` yaml
-$flutter/osx_sdk : >-
- {
- "sdk_version": "14e222b",
- "runtime_versions":
- [
- "ios-16-4_14e222b",
- "ios-16-2_14c18"
- ]
- }
-```
-
-**add_recipes_cq**: String boolean whether to add this target to flutter/recipes CQ. This ensures
-changes to flutter/recipes pass on this target before landing.
-
-**dependencies**: JSON list of objects with "dependency" and optionally "version".
-The list of supported deps is in [flutter_deps recipe_module](https://cs.opensource.google/flutter/recipes/+/master:recipe_modules/flutter_deps/api.py).
-Dependencies generate a corresponding swarming cache that can be used in the
-recipe code. The path of the cache will be the name of the dependency.
-
-Versions can be located in [CIPD](https://chrome-infra-packages.appspot.com/)
-
-Example
-``` yaml
-dependencies: >-
- [
- {"dependency": "android_sdk"},
- {"dependency": "chrome_and_driver", "version": "latest"},
- {"dependency": "clang"},
- {"dependency": "goldctl"}
- ]
-```
-
-**tags**: JSON list of strings. These are currently only used in flutter/flutter to help
-with TESTOWNERSHIP and test flakiness.
-
-Example
-```yaml
-tags: >
- ["devicelab","hostonly"]
-```
-
-**test_timeout_secs** String determining seconds before timeout for an individual test step.
-Note that this is the timeout for a single test step rather than the entire build execution
-timeout.
-
-Example
-``` yaml
-test_timeout_secs: "2700"
-```
-
-**presubmit_max_attempts** The max attempts the target will be auto executed in presubmit. If it is
-not specified, the default value is `1` and it means no auto rerun will happen. If explicitly defined,
-it controls the max number of attempts. For example: `3` means it will be auto rescheduled two more times.
-
-Example
-``` yaml
-presubmit_max_attempts: "3"
-```
-
-### Updating targets
-
-#### Properties
-1. Find the cipd ref to upgrade to
- - If this is a Flutter managed package, look up its docs on uploading a new version
- - For example, JDK is at https://chrome-infra-packages.appspot.com/p/flutter_internal/java/openjdk/linux-amd64
-2. In `ci.yaml`, find a target that would be impacted by this change
- - Override the `version` specified in dependencies
- ```yaml
- - name: Linux Host Engine
- recipe: engine
- properties:
- build_host: "true"
- dependencies: >-
- [
- {"dependency": "open_jdk", "version": "11"}
- ]
- timeout: 60
- ```
- - Send PR, wait for the checks to go green (**the change takes effect on both presubmit and postsubmit as cocoon scheduling**
- **fetches latest change and applies it to new builds immediately**)
-3. If the check is red, add patches to get it green
-4. Once the PR has landed, infrastructure may take 1 or 2 commits to apply the latest properties
- 1. PRs/commits that have rebased on the changing PR do not need to wait
- 2. PRs/commits that have not rebased on the changing PR need to wait
- 3. Local LUCI runs need to wait
- 4. Package cache needs to wait for roll out
-
-**Note:** updates on other entries except `properties` will not take effect immediately. Ths PR needs
-to be landed first to wait for changes propagated in infrastructure.
-
-#### Update target platform
-
-Target depends on the prefix platform in its `name` to decide which platform to run on. This should match
-to an existing platform under `platform_properties`.
-
-If one target needs to switch running platforms, e.g. from a devicelab bot to a host only bot:
-1. Keep the old target entry
-2. Add a new entry under the new platform with
- 1. `bringup: true`
- 2. necessary dependencies
- 3. corresponding tags (tags will only be used for infra metrics analysis)
-3. Land the change with the new entry
-4. If the new target under the new platform passes in postsubmit
- 1. Remove the old target entry and mark the new target as `bringup: false`
-
-Example: say one wants to switch `Linux_android web_size__compile_test` to a vm.
-
-Existing config:
-```yaml
-- name: Linux_android web_size__compile_test
- properties:
- tags: >
- ["devicelab", "android", "linux"]
-```
-
-Add a new config:
-```yaml
-- name: Linux web_size__compile_test
- bringup: true # new target
- properties:
- dependencies: >- # optional
- [
- {"dependency": "new-dependency", "version": "new-dependency-version"}
- ]
- tags: >
- ["devicelab", "hostonly", "linux"]
-```
-
-After validating the new target passes, lands the clean up change by removing the config of old target
-`Linux_android web_size__compile_test` and removing the `bringup: true` for the new target.
-
-Note: this change may affect benchmark metrics. Notify the metrics sherrif to monitor potential regression.
-
-### External Tests
-
-Cocoon supports tests that are not owned by Flutter infrastructure. By default, these should not block the tree but act as FYI to the gardeners.
-
-1. Contact flutter-infra@ with your request (go/flutter-infra-office-hours)
-2. Add your system to SchedulerSystem (https://github.com/flutter/cocoon/blob/master/app_dart/lib/src/model/proto/internal/scheduler.proto)
-3. Add your service account to https://github.com/flutter/cocoon/blob/master/app_dart/lib/src/request_handling/swarming_authentication.dart
-4. Add a custom frontend icon - https://github.com/flutter/cocoon/blob/master/dashboard/lib/widgets/task_icon.dart
-5. Add a custom log link - https://github.com/flutter/cocoon/blob/master/dashboard/lib/logic/qualified_task.dart
-6. Wait for the next prod roll (every weekday)
-7. Add a target to `.ci.yaml`
- ```yaml
- # .ci.yaml
- # Name is an arbitrary string that will show on the build dashboard
- - name: my_external_test_a
- # External tests should not block the tree
- bringup: true
- presubmit: false
- # Scheduler must match what was added to scheduler.proto (any unique name works)
- scheduler: my_external_location
- ```
-8. Send updates to `https://flutter-dashboard.appspot.com/api/update-task-status` - https://github.com/flutter/cocoon/blob/master/app_dart/lib/src/request_handlers/update_task_status.dart
-
-
-## Scheduling Targets
-
-For targets using the Cocoon scheduler, they can run on:
- * Presubmit (via GitHub checks)
- * Postsubmit (via [build dashboard](https://flutter-dashboard.appspot.com/#/build))
-
-By default, all targets should use the Cocoon scheduler.
-
-### Presubmit Features
-
-1. GitHub checks enable targets to run immediately, and are available on the pull request page.
-2. Changes to the ci.yaml will be applied during those presubmit runs.
-3. New targets are required to be brought up with `bringup: true`
-
-### Postsubmit Features
-
-1. Targets are immediately triggered on GitHub webhooks for merged pull requests
-2. Updates are made immediate via LUCI PubSub notifications
-3. Prioritizes recently failed targets (to unblock the tree quicker)
-4. Backfills targets at a low swarming priority when nothing is actively running
-5. Batches targets that have a high queue time, and backfills in off peak hours
-6. Flakiness monitoring
diff --git a/CODEOWNERS b/CODEOWNERS
deleted file mode 100644
index b38f42c..0000000
--- a/CODEOWNERS
+++ /dev/null
@@ -1,44 +0,0 @@
-# Below is a list of Flutter team members' GitHub handles who are
-# suggested reviewers for contributions to this repository.
-#
-# These names are just suggestions. It is fine to have your changes
-# reviewed by someone else.
-
-## app_dart APIs
-app_dart/lib/src/request_handlers/check_flaky_builders.dart @keyonghan
-app_dart/lib/src/request_handlers/create_branch.dart @CaseyHillers
-app_dart/lib/src/request_handlers/dart_internal_subscription.dart @drewroengoogle
-app_dart/lib/src/request_handlers/file_flaky_issue_and_pr.dart @keyonghan
-app_dart/lib/src/request_handlers/flush_cache.dart @keyonghan
-app_dart/lib/src/request_handlers/get_build_status.dart @keyonghan
-app_dart/lib/src/request_handlers/get_release_branches.dart @CaseyHillers
-app_dart/lib/src/request_handlers/get_repos.dart @keyonghan
-app_dart/lib/src/request_handlers/get_status.dart @keyonghan
-app_dart/lib/src/request_handlers/get_green_commits.dart @XilaiZhang
-app_dart/lib/src/request_handlers/github_rate_limit_status.dart @keyonghan
-app_dart/lib/src/request_handlers/github_webhook.dart @keyonghan
-app_dart/lib/src/request_handlers/github/webhook_subscription.dart @keyonghan
-app_dart/lib/src/request_handlers/presubmit_luci_subscription.dart @keyonghan
-app_dart/lib/src/request_handlers/postsubmit_luci_subscription.dart @keyonghan
-app_dart/lib/src/request_handlers/push_build_status_to_github.dart @keyonghan
-app_dart/lib/src/request_handlers/push_gold_status_to_github.dart @Piinks
-app_dart/lib/src/request_handlers/reset_prod_task.dart @keyonghan
-app_dart/lib/src/request_handlers/reset_try_task.dart @keyonghan
-app_dart/lib/src/request_handlers/scheduler/batch_backfiller.dart @keyonghan
-app_dart/lib/src/request_handlers/scheduler/request_subscription.dart @keyonghan
-app_dart/lib/src/request_handlers/scheduler/vacuum_stale_tasks.dart @keyonghan
-app_dart/lib/src/request_handlers/update_existing_flaky_issues.dart @keyonghan
-app_dart/lib/src/request_handlers/update_task_status.dart @keyonghan
-app_dart/lib/src/request_handlers/vacuum_github_commits.dart @keyonghan
-
-## auto_submit app
-auto_submit @ricardoamador
-
-## cipd packages
-cipd_packages/codesign/** @XilaiZhang
-cipd_packages/device_doctor/** @yusuf-goog
-cipd_packages/doxygen/** @gspencergoog
-cipd_packages/ruby/** @godofredoc
-
-## gh_actions
-gh_actions/third_party/no-response/** @godofredoc
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
deleted file mode 100644
index 3bf36c8..0000000
--- a/CONTRIBUTING.md
+++ /dev/null
@@ -1,37 +0,0 @@
-Want to contribute? Great! First, read this page (including the small print at
-the end).
-
-### Before you contribute
-
-Before we can use your code, you must sign the
-[Google Individual Contributor License Agreement](https://cla.developers.google.com/about/google-individual)
-(CLA), which you can do online. The CLA is necessary mainly because you own the
-copyright to your changes, even after your contribution becomes part of our
-codebase, so we need your permission to use and distribute your code. We also
-need to be sure of various other things—for instance that you'll tell us if you
-know that your code infringes on other people's patents. You don't have to sign
-the CLA until after you've submitted your code for review and a member has
-approved it, but you must do it before we can put your code into our codebase.
-
-Before you start working on a larger contribution, you should get in touch with
-us first through the issue tracker with your idea so that we can help out and
-possibly guide you. Coordinating up front makes it much easier to avoid
-frustration later on.
-
-### Code reviews
-
-All submissions, including submissions by project members, require review.
-
-### File headers
-
-All files in the project must start with the following header.
-
- // Copyright (c) 2016, the Flutter project authors. Please see the AUTHORS file
- // for details. All rights reserved. Use of this source code is governed by a
- // BSD-style license that can be found in the LICENSE file.
-
-### The small print
-
-Contributions made by corporations are covered by a different agreement than the
-one above, the
-[Software Grant and Corporate Contributor License Agreement](https://developers.google.com/open-source/cla/corporate).
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index d5384ca..0000000
--- a/LICENSE
+++ /dev/null
@@ -1,27 +0,0 @@
-Copyright 2016 The Flutter Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
deleted file mode 100644
index 07eb0f8..0000000
--- a/README.md
+++ /dev/null
@@ -1,126 +0,0 @@
-<a href="https://github.com/flutter/cocoon">
- <h1 align="center">
- <picture>
- <source media="(prefers-color-scheme: dark)" srcset="https://storage.googleapis.com/cms-storage-bucket/6e19fee6b47b36ca613f.png">
- <img alt="Flutter" src="https://storage.googleapis.com/cms-storage-bucket/c823e53b3a1a7b0d36a9.png">
- </picture>
- </h1>
-</a>
-
-
-[](https://api.securityscorecards.dev/projects/github.com/flutter/cocoon)
-[](https://slsa.dev)
-
-**Cocoon** is a Dart App Engine custom runtime (backend) with a frontend
-of Flutter apps (build and repository dashboard). Cocoon coordinates
-and aggregates the results of [flutter/flutter](https://github.com/flutter/flutter)
-builds.
-
-It is not designed to help developers build Flutter apps.
-
-Cocoon is not a Google product.
-
-
-# Using Cocoon
-
-## Forcing a refresh from GitHub
-
-The server is driven by commits made to
-https://github.com/flutter/flutter repo. It periodically syncs new
-commits. If you need to manually force a refresh, query
-`https://flutter-dashboard.appspot.com/api/refresh-github-commits`.
-
-You will need to be authenticated with Cocoon to do this.
-
-
-# Developing Cocoon
-
-Cocoon has several components:
-
-* A server, which coordinates everything. This is a Dart App Engine
- application. If you have never used that before, you may want to
- [peruse the samples for Dart App
- Engine](https://github.com/dart-lang/appengine_samples). The server
- is found in [app_dart](app_dart/).
-
-* A Flutter app (generally used as a Web app) for the build
- dashboards. The dashboard is found in [dashboard](dashboard/).
-
-Cocoon creates a _checklist_ for each Flutter commit. A checklist is
-made of multiple _tasks_. Tasks are _performed_ by _LUCI bots_.
-
-
-## Getting started
-
-First, [set up a Flutter development
-environment](https://github.com/flutter/flutter/blob/master/CONTRIBUTING.md#developing-for-flutter).
-This will, as a side-effect, provide you with a Dart SDK. Your life
-will be easier if you add that (`.../flutter/bin/cache/dart-sdk/bin/`)
-to your path.
-
-To update the production server, you will need the [Google Cloud
-SDK](https://cloud.google.com/sdk/docs/quickstarts). Since there is no
-Dart SDK, we just use the command line tools.
-
-
-## Developing the server
-
-All the commands in this section assume that you are in the
-`app_dart/` directory.
-
-### Running a local dev server
-
-**This is for legacy users who were granted old security keys. Due to overground, this is no longer supported.**
-
-```sh
-$ export GOOGLE_CLOUD_PROJECT=flutter-dashboard-dev # or flutter-dashboard for prod data
-$ export GCLOUD_KEY=#your_secret # Required for reading/writing from Google Cloud
-$ export COCOON_USE_IN_MEMORY_CACHE=true # Use an in memory cache locally instead of redis to prevent corruption
-$ dart bin/server.dart
-```
-This will output `Serving requests at 0.0.0.0:8080` indicating the server is working.
-
-New requests will be logged to the console.
-
-To develop and test some features, you need to have a local service
-account(key.json) with access to the project you will be connecting to.
-
-If you work for Google you can use the key with flutter-dashboard project
-via [internal doc](https://g3doc.corp.google.com/company/teams/flutter/infrastructure/cocoon/local_development.md?cl=head#test-with-flutter-dashboard-dev-project).
-
-### Deploying a test version on Google Cloud
-
-To run live tests, build the app, and provide instructions for deploying to
-Google App Engine, run this command:
-
-```sh
-dart dev/deploy.dart --project {PROJECT} --version {VERSION}
-```
-
-You can test the new version by accessing
-`{VERSION}-dot-flutter-dashboard.appspot.com` in your browser. If the
-result is satisfactory, the new version can be activated by using the
-Cloud Console UI:
-<https://console.cloud.google.com/appengine/versions?project=flutter-dashboard&serviceId=default>
-
-#### Optional flags
-
-`--profile`: Deploy a profile mode of `dashboard` application for debugging purposes.
-
-`--ignore-version-check`: Ignore the version of Flutter on path (expects to be relatively recent)
-
-
-## Developing the dashboard
-
-The dashboard application will use dummy data when it is not connected
-to the server, so it can be developed locally without a dev server.
-
-To run the dashboard locally, go into the `dashboard` directory and
-run `flutter run -d chrome`. The dashboard will be served from localhost
-(the exact address will be given on the console); copy the URL into
-your browser to view the application. (The dashboard should also be
-able to run on non-Web platforms, but since the Web is our main target
-that is the one that should generally be used for development.)
-
-You can run `flutter packages upgrade` to update the dependencies.
-This may be necessary if you see a failure in the dependencies.
diff --git a/gh_actions/third_party/no-response/action.yml b/action.yml
similarity index 100%
rename from gh_actions/third_party/no-response/action.yml
rename to action.yml
diff --git a/analysis_options.yaml b/analysis_options.yaml
deleted file mode 100644
index d7f6f0f..0000000
--- a/analysis_options.yaml
+++ /dev/null
@@ -1,52 +0,0 @@
-# Specify analysis options for all of flutter/cocoon
-#
-# Until there are meta linter rules, each desired lint must be explicitly enabled.
-# See: https://github.com/dart-lang/linter/issues/288
-#
-# For a list of lints, see: http://dart-lang.github.io/linter/lints/
-# See the configuration guide for more
-# https://github.com/dart-lang/sdk/tree/master/pkg/analyzer#configuring-the-analyzer
-#
-# There are other similar analysis options files in the flutter repos,
-# which should be kept in sync with this file:
-#
-# - analysis_options.yaml (this file)
-# - packages/flutter/lib/analysis_options_user.yaml
-# - https://github.com/flutter/packages/blob/main/analysis_options.yaml
-# - https://github.com/flutter/engine/blob/main/analysis_options.yaml
-#
-# This file contains the analysis options used by Flutter tools, such as IntelliJ,
-# Android Studio, and the `flutter analyze` command.
-include: package:flutter_lints/flutter.yaml
-
-analyzer:
- language:
- strict-casts: false
- strict-raw-types: true
- errors:
- # treat missing required parameters as a warning (not a hint)
- missing_required_param: warning
- # treat missing returns as a warning (not a hint)
- missing_return: warning
- # allow having TODOs in the code
- todo: ignore
- exclude:
- - ".dart_tool/**"
- - "**/*.g.dart"
- - "**/*.pb.dart"
- - "**/*.pbjson.dart"
- - "**/*.pbgrpc.dart"
- - "**/*.pbserver.dart"
- - "**/*.pbenum.dart"
- - "lib/generated_plugin_registrant.dart"
- - "test/**/mocks.mocks.dart"
-
-linter:
- rules:
- use_super_parameters: true
- prefer_final_fields: true
- prefer_final_locals: true
- prefer_single_quotes: true
- require_trailing_commas: true
- unawaited_futures: true
- unnecessary_await_in_return: true
diff --git a/analyze/analyze.dart b/analyze/analyze.dart
deleted file mode 100644
index 5cf5f25..0000000
--- a/analyze/analyze.dart
+++ /dev/null
@@ -1,308 +0,0 @@
-// Copyright 2020 The Flutter 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:convert';
-import 'dart:core';
-import 'dart:io';
-
-import 'package:file/file.dart';
-import 'package:file/local.dart';
-import 'package:path/path.dart' as path;
-
-const FileSystem fs = LocalFileSystem();
-
-// Cocoon's root is the parent of the current working directory,
-final Directory cocoonRoot = fs.currentDirectory.parent;
-
-Future<void> main(List<String> arguments) async {
- print('STARTING ANALYSIS');
- print('cocoonRoot: ${cocoonRoot.path}');
- await run(arguments);
- print('Analysis successful.');
-}
-
-Future<void> run(List<String> arguments) async {
- bool assertsEnabled = false;
- assert(() {
- assertsEnabled = true;
- return true;
- }());
- if (!assertsEnabled) {
- exitWithError(<String>['The analyze.dart script must be run with --enable-asserts.']);
- }
-
- print('Trailing spaces...');
- await verifyNoTrailingSpaces(cocoonRoot.path);
-
- print('Executable allowlist...');
- await _checkForNewExecutables();
-
- print('Proto analysis...');
- await verifyProtos(cocoonRoot);
-}
-
-// TESTS
-
-Future<void> verifyNoTrailingSpaces(
- String workingDirectory, {
- int minimumMatches = 100,
-}) async {
- final List<File> files = await _allFiles(workingDirectory, null, minimumMatches: minimumMatches)
- .where((File file) => path.basename(file.path) != 'serviceaccount.enc')
- .where((File file) => path.basename(file.path) != 'Ahem.ttf')
- .where((File file) => path.extension(file.path) != '.snapshot')
- .where((File file) => path.extension(file.path) != '.png')
- .where((File file) => path.extension(file.path) != '.jpg')
- .where((File file) => path.extension(file.path) != '.ico')
- .where((File file) => path.extension(file.path) != '.jar')
- .where((File file) => path.extension(file.path) != '.swp')
- .where((File file) => !path.basename(file.path).endsWith('pbserver.dart'))
- .where((File file) => !path.basename(file.path).endsWith('pb.dart'))
- .where((File file) => !path.basename(file.path).endsWith('pbenum.dart'))
- .toList();
- final List<String> problems = <String>[];
- for (final File file in files) {
- final List<String> lines = file.readAsLinesSync();
- for (int index = 0; index < lines.length; index += 1) {
- if (lines[index].endsWith(' ')) {
- problems.add('${file.path}:${index + 1}: trailing U+0020 space character');
- } else if (lines[index].endsWith('\t')) {
- problems.add('${file.path}:${index + 1}: trailing U+0009 tab character');
- }
- }
- if (lines.isNotEmpty && lines.last == '') problems.add('${file.path}:${lines.length}: trailing blank line');
- }
- if (problems.isNotEmpty) exitWithError(problems);
-}
-
-Future<void> verifyProtos(Directory workingDirectory) async {
- final List<String> errors = <String>[];
- final List<File> protos = await _allFiles(workingDirectory.path, 'proto', minimumMatches: 1).toList();
- for (final File proto in protos) {
- final String content = proto.readAsStringSync();
- if (!content.contains(RegExp(r'package\ \w+;'))) {
- errors.add('${proto.path} requires a package (https://protobuf.dev/programming-guides/proto2/#packages)');
- }
- }
-
- if (errors.isNotEmpty) {
- exitWithError(<String>[
- 'The following files are missing package declarations:',
- ...errors,
- ]);
- }
-}
-
-// UTILITY FUNCTIONS
-
-Future<List<File>> _gitFiles(String workingDirectory, {bool runSilently = true}) async {
- final EvalResult evalResult = await _evalCommand(
- 'git',
- <String>['ls-files', '-z'],
- workingDirectory: workingDirectory,
- runSilently: runSilently,
- );
- if (evalResult.exitCode != 0) {
- exitWithError(<String>[
- 'git ls-files failed with exit code ${evalResult.exitCode}',
- 'stdout:',
- evalResult.stdout,
- 'stderr:',
- evalResult.stderr,
- ]);
- }
- final List<String> filenames = evalResult.stdout.split('\x00');
- assert(filenames.last.isEmpty); // git ls-files gives a trailing blank 0x00
- filenames.removeLast();
- return filenames.map<File>((String filename) => fs.file(path.join(workingDirectory, filename))).toList();
-}
-
-Stream<File> _allFiles(String workingDirectory, String? extension, {required int minimumMatches}) async* {
- final Set<String> gitFileNamesSet = <String>{};
- gitFileNamesSet.addAll((await _gitFiles(workingDirectory)).map((File f) => path.canonicalize(f.absolute.path)));
-
- assert(extension == null || !extension.startsWith('.'), 'Extension argument should not start with a period.');
- final Set<FileSystemEntity> pending = <FileSystemEntity>{fs.directory(workingDirectory)};
- int matches = 0;
- while (pending.isNotEmpty) {
- final FileSystemEntity entity = pending.first;
- pending.remove(entity);
- if (path.extension(entity.path) == '.tmpl') continue;
- if (entity is File) {
- if (!gitFileNamesSet.contains(path.canonicalize(entity.absolute.path))) continue;
- if (path.basename(entity.path) == 'flutter_export_environment.sh') continue;
- if (path.basename(entity.path) == 'gradlew.bat') continue;
- if (path.basename(entity.path) == '.DS_Store') continue;
- if (extension == null || path.extension(entity.path) == '.$extension') {
- matches += 1;
- yield entity;
- }
- } else if (entity is Directory) {
- if (fs.file(path.join(entity.path, '.dartignore')).existsSync()) continue;
- if (path.basename(entity.path) == '.git') continue;
- if (path.basename(entity.path) == '.idea') continue;
- if (path.basename(entity.path) == '.gradle') continue;
- if (path.basename(entity.path) == '.dart_tool') continue;
- if (path.basename(entity.path) == '.idea') continue;
- if (path.basename(entity.path) == 'build') continue;
- pending.addAll(entity.listSync());
- }
- }
- assert(
- matches >= minimumMatches,
- 'Expected to find at least $minimumMatches files with extension ".$extension" in "$workingDirectory", but only found $matches.',
- );
-}
-
-class EvalResult {
- EvalResult({
- required this.stdout,
- required this.stderr,
- this.exitCode = 0,
- });
-
- final String stdout;
- final String stderr;
- final int exitCode;
-}
-
-Future<EvalResult> _evalCommand(
- String executable,
- List<String> arguments, {
- required String workingDirectory,
- Map<String, String>? environment,
- bool allowNonZeroExit = false,
- bool runSilently = false,
-}) async {
- final String commandDescription = '${path.relative(executable, from: workingDirectory)} ${arguments.join(' ')}';
- final String relativeWorkingDir = path.relative(workingDirectory);
-
- if (!runSilently) {
- print('RUNNING $relativeWorkingDir $commandDescription');
- }
-
- final Stopwatch time = Stopwatch()..start();
- final Process process = await Process.start(
- executable,
- arguments,
- workingDirectory: workingDirectory,
- environment: environment,
- );
-
- final Future<List<List<int>>> savedStdout = process.stdout.toList();
- final Future<List<List<int>>> savedStderr = process.stderr.toList();
- final int exitCode = await process.exitCode;
- final EvalResult result = EvalResult(
- stdout: utf8.decode((await savedStdout).expand<int>((List<int> ints) => ints).toList()),
- stderr: utf8.decode((await savedStderr).expand<int>((List<int> ints) => ints).toList()),
- exitCode: exitCode,
- );
-
- if (!runSilently) {
- print('ELAPSED TIME: ${time.elapsed} for $commandDescription in $relativeWorkingDir');
- }
-
- if (exitCode != 0 && !allowNonZeroExit) {
- stderr.write(result.stderr);
- exitWithError(<String>[
- 'ERROR: Last command exited with $exitCode.',
- 'Command: $commandDescription',
- 'Relative working directory: $relativeWorkingDir',
- ]);
- }
-
- return result;
-}
-
-// These files legitimately require executable permissions
-const Set<String> kExecutableAllowlist = <String>{
- 'app_dart/tool/build.sh',
- 'cipd_packages/codesign/tool/build.sh',
- 'cipd_packages/device_doctor/tool/build.sh',
- 'cipd_packages/doxygen/tool/build.sh',
- 'cloud_build/dashboard_build.sh',
- 'cloud_build/deploy_app_dart.sh',
- 'cloud_build/deploy_auto_submit.sh',
- 'cloud_build/deploy_cron_jobs.sh',
- 'cloud_build/get_docker_image_provenance.sh',
- 'cloud_build/verify_provenance.sh',
- 'dashboard/regen_mocks.sh',
- 'dev/provision_salt.sh',
- 'dev/prs_to_main.sh',
- 'format.sh',
- 'packages/buildbucket-dart/tool/regenerate.sh',
- 'test_utilities/bin/analyze.sh',
- 'test_utilities/bin/config_test_runner.sh',
- 'test_utilities/bin/dart_test_runner.sh',
- 'test_utilities/bin/flutter_test_runner.sh',
- 'test_utilities/bin/global_test_runner.dart',
- 'test_utilities/bin/licenses.sh',
- 'test_utilities/bin/prepare_environment.sh',
-};
-
-const String kShebangRegex = r'#!/usr/bin/env (bash|sh)';
-
-Future<void> _checkForNewExecutables() async {
- // 0b001001001
- const int executableBitMask = 0x49;
- final List<File> files = await _gitFiles(cocoonRoot.path);
- final List<String> relativePaths = files.map<String>((File file) {
- return path.relative(
- file.path,
- from: cocoonRoot.path,
- );
- }).toList();
- for (String allowed in kExecutableAllowlist) {
- if (!relativePaths.contains(allowed)) {
- throw Exception(
- 'File $allowed in kExecutableAllowlist in analyze/analyze.dart '
- 'does not exist. Please fix path or remove from kExecutableAllowlist.',
- );
- }
- }
- int unexpectedExecutableCount = 0;
- int unexpectedShebangShellCount = 0;
- for (final File file in files) {
- final String relativePath = path.relative(
- file.path,
- from: cocoonRoot.path,
- );
- final FileStat stat = file.statSync();
- final bool isExecutable = stat.mode & executableBitMask != 0x0;
- final bool inAllowList = kExecutableAllowlist.contains(relativePath);
- if (isExecutable && !inAllowList) {
- unexpectedExecutableCount += 1;
- print('$relativePath is executable: ${(stat.mode & 0x1FF).toRadixString(2)}');
- }
- if (inAllowList && file.path.endsWith('.sh')) {
- final String shebang = file.readAsLinesSync().first;
- if (!shebang.startsWith(RegExp(kShebangRegex))) {
- unexpectedShebangShellCount += 1;
- print("$relativePath has the initial line of $shebang, which doesn't match '$kShebangRegex'");
- }
- }
- }
- if (unexpectedExecutableCount > 0) {
- throw Exception(
- 'found $unexpectedExecutableCount unexpected executable file'
- '${unexpectedExecutableCount == 1 ? '' : 's'}! If this was intended, you '
- 'must add this file to kExecutableAllowlist in analyze/analyze.dart',
- );
- }
- if (unexpectedShebangShellCount > 0) {
- throw Exception(
- 'found $unexpectedShebangShellCount unexpected shell #! line'
- '${unexpectedShebangShellCount == 1 ? '' : 's'}! If this was intended, you '
- 'must modify kShebangRegex in analyze/analyze.dart',
- );
- }
-}
-
-void exitWithError(List<String> messages) {
- final String line = '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━';
- print(line);
- messages.forEach(print);
- print(line);
- exit(1);
-}
diff --git a/analyze/pubspec.yaml b/analyze/pubspec.yaml
deleted file mode 100644
index 46bce17..0000000
--- a/analyze/pubspec.yaml
+++ /dev/null
@@ -1,15 +0,0 @@
-name: cocoon_analyze
-description: Cocoon static analysis scripts
-publish_to: none
-
-environment:
- sdk: ">=2.18.0 <4.0.0"
-
-dependencies:
- file: 7.0.0
- path: 1.8.3
- platform: 3.1.3
-
-dev_dependencies:
- mockito: 5.4.2
- test_api: 0.6.1
diff --git a/app_dart/.gitignore b/app_dart/.gitignore
deleted file mode 100644
index 3c8a157..0000000
--- a/app_dart/.gitignore
+++ /dev/null
@@ -1,6 +0,0 @@
-# Files and directories created by pub.
-.dart_tool/
-.packages
-
-# Conventional directory for build output.
-build/
diff --git a/app_dart/Dockerfile b/app_dart/Dockerfile
deleted file mode 100644
index 27652a3..0000000
--- a/app_dart/Dockerfile
+++ /dev/null
@@ -1,17 +0,0 @@
-# Copyright 2022 The Flutter Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-
-# Dart Docker official images can be found here: https://hub.docker.com/_/dart
-FROM dart:beta@sha256:88ced76ff4a63e565872df26fe2442f060e3ecf828a272090ad10c79e9d044af
-
-WORKDIR /app
-
-# Copy app source code (except anything in .dockerignore).
-COPY . .
-RUN dart pub get
-
-# Start server.
-EXPOSE 8080
-CMD ["/usr/lib/dart/bin/dart", "/app/bin/server.dart"]
diff --git a/app_dart/LICENSE b/app_dart/LICENSE
deleted file mode 100644
index d5384ca..0000000
--- a/app_dart/LICENSE
+++ /dev/null
@@ -1,27 +0,0 @@
-Copyright 2016 The Flutter Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/app_dart/README.md b/app_dart/README.md
deleted file mode 100644
index d135d79..0000000
--- a/app_dart/README.md
+++ /dev/null
@@ -1,166 +0,0 @@
-# Dart backend for cocoon
-
-This folder contains a Dart based backend for Cocoon.
-
-## Building and running
-
-### Prerequisites
-
-* Install the Google Cloud Developer Tools Command Line Interface
-([`gcloud`](https://cloud.google.com/sdk/docs/quickstarts)). Then initialize it
-and authenticate yourself by running:
-
-```sh
-gcloud auth login
-gcloud init
-```
-* [Install Flutter](https://flutter.dev/docs/get-started/install )
-```sh
-export PATH="$PATH":"path/to/flutter/bin/"
-flutter upgrade
-flutter pub get
-export PATH="$PATH":"path/to/flutter/bin/cache/dart-sdk/bin/"
-```
-
-### Running the tests
-
-```sh
-$ dart test
-```
-
-### Running codegen
-
-#### JSON
-
-To update the JSON serialization generated code, run:
-
-```sh
-$ dart run build_runner build
-```
-
-Any updates should be checked into source control.
-
-#### Protobuf
-
-To update the Protocol Buffer generated code:
-
-1. [Download](https://github.com/protocolbuffers/protobuf/releases) and install
- the protocol buffer compiler (`protoc`). Once installed, update your `PATH`
- to include the path to the `protoc` binary.
-
- On Linux, use `sudo apt-get install protocol-compiler` to install.
- On macOS, use `brew install protobuf`
-
-2. Install the [`protoc_plugin`](https://pub.dev/packages/protoc_plugin) Dart
- package. Once installed, update your `PATH` to include the path to the
- `protoc_plugin/bin` directory (or `$HOME/.pub-cache/bin` if you used
- `pub global activate protoc_plugin`).
-
-3. Run the following command:
-
- ```sh
- $ protoc --dart_out=. lib/src/model/proto/**/*.proto
- ```
-
-4. Remove the unused generated files:
-
- ```sh
- $ find . -regex '.*\.\(pbjson\|pbserver\)\.dart' -delete
- ```
- (you can remove the `*.pbenum.dart` files too, except for protobuffers that actually define enums,
- like `build_status_response.proto`)
-
-### Generating cloud datastore indexes
-
-To update the indexes in the App Engine project, run:
-
-```sh
-$ gcloud datastore indexes create index.yaml
-```
-
-### Local development
-
-#### Using physical machine
-
-* Setting up the environment
-
-```sh
-export COCOON_USE_IN_MEMORY_CACHE=true
-```
-
-This environment is needed as you don't have access to the remote redis
-instance during local development.
-
-* Starting server
-
-```sh
-export COCOON_USE_IN_MEMORY_CACHE=true
-dart bin/server.dart
-```
-
-If you see Serving requests at 0.0.0.0:8080 the dev server is working.
-
-#### Using Docker
-
-* Running a local development instance
-
-Once you've installed Docker and have the `docker` command-line tool in
-your path, then you can use the following commands to build, run, stop,
-and kill a local development instance.
-
-```sh
-# Build the docker image
-$ docker build -t local .
-
-# Start the local container, clearing the console buffer and tailing the logs
-$ container_id="$(docker run -d -p 8080:8080 local)" && \
- clear && \
- printf '\e[3J' && \
- docker logs $container_id -f
-
-# Stop the local Docker container
-$ docker container ls|grep local|tr -s ' '|cut -d' ' -f1|xargs docker container stop
-
-# Remove the local Docker image
-$ docker images|grep local|tr -s ' '|cut -d' ' -f3|xargs docker rmi -f
-```
-
-* ssh into instance
-
-```sh
-$ docker exec -it <container name> /bin/bash
-```
-
-### Deploying a release to App Engine
-
-#### [Auto-deploy](go/cocoon-cloud-build#auto-deploy)
-Cocoon auto deployment has been set up via
-[Google Cloud Build](https://console.cloud.google.com/cloud-build/triggers?project=flutter-dashboard)
-daily on Workdays.
-
-#### [Manual-deploy(go/cocoon-cloud-build#manual-deploy)
-
-* Using the cloud build
-
-This is easy to deploy if you simply want a new version based on
-the latest commit. Open
-[Cloud Build dashboard](https://console.cloud.google.com/cloud-build/triggers?project=flutter-dashboard)
-and click run in the push-master trigger ([example](https://screenshot.googleplex.com/4DDy4XdVQxMKqCd))
-
-* Using a cocoon checkout
-Let `PROJECT_ID` be the Google Cloud Project ID and `VERSION` be the version you're deploying to App Engine. Visit
-https://console.cloud.google.com/appengine/versions?project=flutter-dashboard
-for the list of current versions.
-
-```sh
-$ dart dev/deploy.dart --version version-$(git rev-parse --short HEAD) --project flutter-dashboard
-```
-
-The deploy script will build the Flutter project and copy it over for deployment.
-Then it will use the Google Cloud CLI to deploy the project to AppEngine.
-
-For more options run:
-
-```sh
-$ dart dev/deploy.dart --help
-```
diff --git a/app_dart/analysis_options.yaml b/app_dart/analysis_options.yaml
deleted file mode 100644
index f4cf71f..0000000
--- a/app_dart/analysis_options.yaml
+++ /dev/null
@@ -1,8 +0,0 @@
-include: ../analysis_options.yaml
-
-linter:
- rules:
- # a few rules listed below are the ones we would like to exclude from flutter_lint package, for app_dart
- # reasons for exclusions are provided in the comments to the right
- avoid_print: false # we have necessary print calls in the code
- constant_identifier_names: false # we have all capitalized enums in check_for_waiting_pull_requests_test.dart
diff --git a/app_dart/app.yaml b/app_dart/app.yaml
deleted file mode 100644
index 2a3ec5b..0000000
--- a/app_dart/app.yaml
+++ /dev/null
@@ -1,33 +0,0 @@
-# Copyright 2019 The Flutter Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-runtime: custom
-env: flex
-service: default
-
-readiness_check:
- path: "/readiness_check"
- check_interval_sec: 20
- timeout_sec: 20
- failure_threshold: 10
- success_threshold: 2
- app_start_timeout_sec: 600
-
-resources:
- memory_gb: 4.0
-
-handlers:
-# The Dart server handles all requests. However, this is used to ensure the
-# assets the dart server needs are uploaded to AppEngine.
-- url: /v2/(.*\.(html|css|js|ico|svg|png|jpg|map))$
- application_readable: true # So the dart server can read the files
- # If the Dart custom runtime changes and starts using this handler,
- # we want to know. This will have the app only serve HTML and we
- # will know we can remove the Dart handling code. Just swap
- # index.html to \1
- static_files: build/web/index.html
- # app_flutter's build files needed to be copied over to this project. This
- # is because the Google Cloud utility cannot go outside the scope of app_dart.
- # Navigating to ../app_flutter/build/web will silently error.
- upload: build/web/.*\.(html|css|js|ico|svg|png|jpg|map)$
diff --git a/app_dart/bin/generate_jspb.dart b/app_dart/bin/generate_jspb.dart
deleted file mode 100644
index f48082b..0000000
--- a/app_dart/bin/generate_jspb.dart
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright 2021 The Flutter 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:async';
-import 'dart:convert';
-import 'dart:io';
-
-import 'package:cocoon_service/protos.dart' as pb;
-import 'package:github/github.dart';
-import 'package:http/http.dart' as http;
-import 'package:yaml/yaml.dart';
-
-Future<String> githubFileContent(
- RepositorySlug slug,
- String filePath, {
- String ref = 'master',
- Duration timeout = const Duration(seconds: 5),
-}) async {
- final Uri githubUrl = Uri.https('raw.githubusercontent.com', '${slug.fullName}/$ref/$filePath');
- return getUrl(githubUrl);
-}
-
-FutureOr<String> getUrl(Uri url) async {
- final http.Client client = http.Client();
- try {
- final http.Response response = await client.get(url);
-
- if (response.statusCode == HttpStatus.ok) {
- return response.body;
- } else {
- throw HttpException('HTTP ${response.statusCode}: $url');
- }
- } finally {
- client.close();
- }
-}
-
-Future<String> getRemoteConfigContent(String repo, String ref) async {
- final String configContent = await githubFileContent(
- RepositorySlug('flutter', repo),
- '.ci.yaml',
- ref: ref,
- );
- return configContent;
-}
-
-String getLocalConfigContent(String path) {
- final File configFile = File(path);
- return configFile.readAsStringSync();
-}
-
-Future<void> main(List<String> args) async {
- if (args.length != 1 && args.length != 2) {
- print('generate_jspb.dart \$local_ci_yaml');
- print('generate_jspb.dart \$repo \$sha');
- exit(1);
- }
- String configContent;
- if (args.length == 2) {
- configContent = await getRemoteConfigContent(args[0], args[1]);
- } else {
- configContent = getLocalConfigContent(args[0]);
- }
-
- final YamlMap configYaml = loadYaml(configContent) as YamlMap;
- final pb.SchedulerConfig schedulerConfig = pb.SchedulerConfig()..mergeFromProto3Json(configYaml);
-
- print(jsonEncode(schedulerConfig.toProto3Json()));
-}
diff --git a/app_dart/bin/server.dart b/app_dart/bin/server.dart
deleted file mode 100644
index e278599..0000000
--- a/app_dart/bin/server.dart
+++ /dev/null
@@ -1,320 +0,0 @@
-// Copyright 2019 The Flutter 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 'dart:math';
-
-import 'package:appengine/appengine.dart';
-import 'package:cocoon_service/cocoon_service.dart';
-import 'package:cocoon_service/src/service/commit_service.dart';
-import 'package:gcloud/db.dart';
-
-/// For local development, you might want to set this to true.
-const String _kCocoonUseInMemoryCache = 'COCOON_USE_IN_MEMORY_CACHE';
-
-Future<void> main() async {
- await withAppEngineServices(() async {
- useLoggingPackageAdaptor();
-
- final bool inMemoryCache = Platform.environment[_kCocoonUseInMemoryCache] == 'true';
- final CacheService cache = CacheService(inMemory: inMemoryCache);
-
- final Config config = Config(dbService, cache);
- final AuthenticationProvider authProvider = AuthenticationProvider(config: config);
- final AuthenticationProvider swarmingAuthProvider = SwarmingAuthenticationProvider(config: config);
- final BuildBucketClient buildBucketClient = BuildBucketClient(
- accessTokenService: AccessTokenService.defaultProvider(config),
- );
-
- /// LUCI service class to communicate with buildBucket service.
- final LuciBuildService luciBuildService = LuciBuildService(
- config: config,
- cache: cache,
- buildBucketClient: buildBucketClient,
- pubsub: const PubSub(),
- );
-
- /// Github checks api service used to provide luci test execution status on the Github UI.
- final GithubChecksService githubChecksService = GithubChecksService(
- config,
- );
-
- // Gerrit service class to communicate with GoB.
- final GerritService gerritService = GerritService(config: config);
-
- /// Cocoon scheduler service to manage validating commits in presubmit and postsubmit.
- final Scheduler scheduler = Scheduler(
- cache: cache,
- config: config,
- githubChecksService: githubChecksService,
- luciBuildService: luciBuildService,
- );
-
- final BranchService branchService = BranchService(
- config: config,
- gerritService: gerritService,
- );
-
- final CommitService commitService = CommitService(config: config);
-
- final Map<String, RequestHandler<dynamic>> handlers = <String, RequestHandler<dynamic>>{
- '/api/check_flaky_builders': CheckFlakyBuilders(
- config: config,
- authenticationProvider: authProvider,
- ),
- '/api/create-branch': CreateBranch(
- branchService: branchService,
- config: config,
- authenticationProvider: authProvider,
- ),
- '/api/dart-internal-subscription': DartInternalSubscription(
- cache: cache,
- config: config,
- buildBucketClient: buildBucketClient,
- ),
- '/api/file_flaky_issue_and_pr': FileFlakyIssueAndPR(
- config: config,
- authenticationProvider: authProvider,
- ),
- '/api/flush-cache': FlushCache(
- config: config,
- authenticationProvider: authProvider,
- cache: cache,
- ),
- '/api/github-webhook-pullrequest': GithubWebhook(
- config: config,
- pubsub: const PubSub(),
- secret: config.webhookKey,
- topic: 'github-webhooks',
- ),
- // TODO(chillers): Move to release service. https://github.com/flutter/flutter/issues/132082
- '/api/github/frob-webhook': GithubWebhook(
- config: config,
- pubsub: const PubSub(),
- secret: config.frobWebhookKey,
- topic: 'frob-webhooks',
- ),
- '/api/github/webhook-subscription': GithubWebhookSubscription(
- config: config,
- cache: cache,
- gerritService: gerritService,
- githubChecksService: githubChecksService,
- scheduler: scheduler,
- commitService: commitService,
- ),
- '/api/presubmit-luci-subscription': PresubmitLuciSubscription(
- cache: cache,
- config: config,
- buildBucketClient: buildBucketClient,
- luciBuildService: luciBuildService,
- githubChecksService: githubChecksService,
- scheduler: scheduler,
- ),
- '/api/postsubmit-luci-subscription': PostsubmitLuciSubscription(
- cache: cache,
- config: config,
- scheduler: scheduler,
- githubChecksService: githubChecksService,
- ),
- '/api/push-build-status-to-github': PushBuildStatusToGithub(
- config: config,
- authenticationProvider: authProvider,
- ),
- '/api/push-gold-status-to-github': PushGoldStatusToGithub(
- config: config,
- authenticationProvider: authProvider,
- ),
- '/api/reset-prod-task': ResetProdTask(
- config: config,
- authenticationProvider: authProvider,
- luciBuildService: luciBuildService,
- scheduler: scheduler,
- ),
- '/api/reset-try-task': ResetTryTask(
- config: config,
- authenticationProvider: authProvider,
- scheduler: scheduler,
- ),
- '/api/scheduler/batch-backfiller': BatchBackfiller(
- config: config,
- scheduler: scheduler,
- ),
- '/api/scheduler/batch-request-subscription': SchedulerRequestSubscription(
- cache: cache,
- config: config,
- buildBucketClient: buildBucketClient,
- ),
- '/api/scheduler/vacuum-stale-tasks': VacuumStaleTasks(
- config: config,
- ),
- '/api/update_existing_flaky_issues': UpdateExistingFlakyIssue(
- config: config,
- authenticationProvider: authProvider,
- ),
-
- /// Updates task related details.
- ///
- /// This API updates task status in datastore and
- /// pushes performance metrics to skia-perf.
- ///
- /// POST: /api-update-status
- ///
- /// Parameters:
- /// CommitBranch: (string in body). Branch of commit.
- /// CommitSha: (string in body). Sha of commit.
- /// BuilderName: (string in body). Name of the luci builder.
- /// NewStatus: (string in body) required. Status of the task.
- /// ResultData: (string in body) optional. Benchmark data.
- /// BenchmarkScoreKeys: (string in body) optional. Benchmark data.
- ///
- /// Response: Status 200 OK
- '/api/update-task-status': UpdateTaskStatus(
- config: config,
- authenticationProvider: swarmingAuthProvider,
- ),
- '/api/vacuum-github-commits': VacuumGithubCommits(
- config: config,
- authenticationProvider: authProvider,
- scheduler: scheduler,
- ),
-
- /// Returns status of the framework tree.
- ///
- /// Returns serialized proto with enum representing the
- /// status of the tree and list of offending tasks.
- ///
- /// GET: /api/public/build-status
- ///
- /// Parameters:
- /// branch: (string in query) default: 'master'. Name of the repo branch.
- ///
- /// Response: Status 200 OK
- /// Returns [BuildStatusResponse]:
- /// {
- /// 1: 2,
- /// 2: [ "win_tool_tests_commands", "win_build_test", "win_module_test"]
- /// }
- '/api/public/build-status': CacheRequestHandler<Body>(
- cache: cache,
- config: config,
- delegate: GetBuildStatus(config: config),
- ttl: const Duration(seconds: 15),
- ),
- '/api/public/get-release-branches': CacheRequestHandler<Body>(
- cache: cache,
- config: config,
- delegate: GetReleaseBranches(config: config, branchService: branchService),
- ttl: const Duration(hours: 1),
- ),
-
- /// Returns task results for commits.
- ///
- /// Returns result details about each task in each checklist for every commit.
- ///
- /// GET: /api/public/get-status
- ///
- /// Parameters:
- /// branch: (string in query) default: 'master'. Name of the repo branch.
- /// lastCommitKey: (string in query) optional. Encoded commit key for the last commit to return resutls.
- ///
- /// Response: Status: 200 OK
- /// {"Statuses":[
- /// {"Checklist":{
- /// "Key":"ah..jgM",
- /// "Checklist":{"FlutterRepositoryPath":"flutter/flutter",
- /// "CreateTimestamp":1620134239000,
- /// "Commit":{"Sha":"7f1d1414cc5f0b0317272ced49a9c0b44e5c3af8",
- /// "Message":"Revert \"Migrate to ChannelBuffers.push\"",
- /// "Author":{"Login":"renyou","avatar_url":"https://avatars.githubusercontent.com/u/666474?v=4"}},"Branch":"master"}},
- /// "Stages":[{"Name":"chromebot",
- /// "Tasks":[
- /// {"Task":{
- /// "ChecklistKey":"ahF..jgM",
- /// "CreateTimestamp":1620134239000,
- /// "StartTimestamp":0,
- /// "EndTimestamp":1620136203757,
- /// "Name":"linux_cubic_bezier_perf__e2e_summary",
- /// "Attempts":1,
- /// "Flaky":false,
- /// "TimeoutInMinutes":0,
- /// "Reason":"",
- /// "BuildNumber":null,
- /// "BuildNumberList":"1279",
- /// "BuilderName":"Linux cubic_bezier_perf__e2e_summary",
- /// "luciBucket":"luci.flutter.prod",
- /// "RequiredCapabilities":["can-update-github"],
- /// "ReservedForAgentID":"",
- /// "StageName":"chromebot",
- /// "Status":"Succeeded"
- /// },
- /// ],
- /// "Status": "InProgress",
- /// ]},
- /// },
- /// }
- '/api/public/get-status': CacheRequestHandler<Body>(
- cache: cache,
- config: config,
- delegate: GetStatus(config: config),
- ),
-
- '/api/public/get-green-commits': GetGreenCommits(config: config),
-
- /// Record GitHub API quota usage in BigQuery.
- ///
- /// Pushes data to BigQuery for metric collection to
- /// analyze usage over time.
- ///
- /// This api is called via cron job.
- ///
- /// GET: /api/public/github-rate-limit-status
- ///
- /// Response: Status 200 OK
- '/api/public/github-rate-limit-status': CacheRequestHandler<Body>(
- config: config,
- cache: cache,
- ttl: const Duration(minutes: 1),
- delegate: GithubRateLimitStatus(config: config),
- ),
- '/api/public/repos': GetRepos(config: config),
-
- /// Handler for AppEngine to identify when dart server is ready to serve requests.
- '/readiness_check': ReadinessCheck(config: config),
- };
-
- return runAppEngine(
- (HttpRequest request) async {
- if (handlers.containsKey(request.uri.path)) {
- final RequestHandler<dynamic> handler = handlers[request.uri.path]!;
- await handler.service(request);
- } else {
- /// Requests with query parameters and anchors need to be trimmed to get the file path.
- // TODO(chillers): Use toFilePath(), https://github.com/dart-lang/sdk/issues/39373
- final int queryIndex =
- request.uri.path.contains('?') ? request.uri.path.indexOf('?') : request.uri.path.length;
- final int anchorIndex =
- request.uri.path.contains('#') ? request.uri.path.indexOf('#') : request.uri.path.length;
-
- /// Trim to the first instance of an anchor or query.
- final int trimIndex = min(queryIndex, anchorIndex);
- final String filePath = request.uri.path.substring(0, trimIndex);
-
- const Map<String, String> redirects = <String, String>{
- '/build.html': '/#/build',
- };
- if (redirects.containsKey(filePath)) {
- request.response.statusCode = HttpStatus.permanentRedirect;
- return request.response.redirect(Uri.parse(redirects[filePath]!));
- }
-
- await StaticFileHandler(filePath, config: config).service(request);
- }
- },
- onAcceptingConnections: (InternetAddress address, int port) {
- final String host = address.isLoopback ? 'localhost' : address.host;
- print('Serving requests at http://$host:$port/');
- },
- );
- });
-}
diff --git a/app_dart/bin/validate_scheduler_config.dart b/app_dart/bin/validate_scheduler_config.dart
deleted file mode 100644
index 3b3fb11..0000000
--- a/app_dart/bin/validate_scheduler_config.dart
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2021 The Flutter 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:cocoon_service/protos.dart' as pb;
-import 'package:cocoon_service/src/model/ci_yaml/ci_yaml.dart';
-import 'package:cocoon_service/src/service/config.dart';
-import 'package:yaml/yaml.dart';
-
-void main(List<String> args) async {
- if (args.length != 1) {
- print('validate_scheduler_config.dart configPath');
- exit(1);
- }
- final String configPath = args.first;
- final File configFile = File(configPath);
- if (!configFile.existsSync()) {
- print('validate_scheduler_config.dart configPath');
- exit(1);
- }
-
- final YamlMap configYaml = loadYaml(configFile.readAsStringSync()) as YamlMap;
- final pb.SchedulerConfig unCheckedSchedulerConfig = pb.SchedulerConfig()..mergeFromProto3Json(configYaml);
- print(
- CiYaml(
- slug: Config.flutterSlug,
- branch: Config.defaultBranch(Config.flutterSlug),
- config: unCheckedSchedulerConfig,
- ).config,
- );
-}
diff --git a/app_dart/bin/validate_task_ownership.dart b/app_dart/bin/validate_task_ownership.dart
deleted file mode 100644
index f3e18c4..0000000
--- a/app_dart/bin/validate_task_ownership.dart
+++ /dev/null
@@ -1,75 +0,0 @@
-// Copyright 2021 The Flutter 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' as io;
-
-import 'package:cocoon_service/cocoon_service.dart';
-import 'package:cocoon_service/src/foundation/utils.dart';
-import 'package:file/file.dart';
-import 'package:file/local.dart';
-import 'package:github/github.dart';
-import 'package:http/http.dart' as http;
-
-/// Remote check based on flutter `repo` and the commit `ref`.
-///
-/// This currently supports `flutter/flutter` only.
-Future<List<String>> remoteCheck(String repo, String ref) async {
- final String ciYamlContent = await githubFileContent(
- RepositorySlug('flutter', repo),
- kCiYamlPath,
- httpClientProvider: () => http.Client(),
- ref: ref,
- );
- final String testOwnersContent = await githubFileContent(
- RepositorySlug('flutter', repo),
- kTestOwnerPath,
- httpClientProvider: () => http.Client(),
- ref: ref,
- );
-
- final List<String> noOwnerBuilders = validateOwnership(ciYamlContent, testOwnersContent, unfilteredTargets: true);
- return noOwnerBuilders;
-}
-
-/// Local check is based on paths to the local `.ci.yaml` and `TESTOWNERS` files.
-List<String> localCheck(String ciYamlPath, String testOwnersPath) {
- const FileSystem fs = LocalFileSystem();
- final File ciYamlFile = fs.file(ciYamlPath);
- final File testOwnersFile = fs.file(testOwnersPath);
- if (!ciYamlFile.existsSync() || !testOwnersFile.existsSync()) {
- print('Make sure ciYamlPath and testOwnersPath exist.');
- io.exit(1);
- }
- final List<String> noOwnerBuilders =
- validateOwnership(ciYamlFile.readAsStringSync(), testOwnersFile.readAsStringSync(), unfilteredTargets: true);
- return noOwnerBuilders;
-}
-
-/// Validates task ownership.
-///
-/// It expects two parameters for remote validation: the flutter `repo` and the `commit`.
-///
-/// It expects three parameters for local validation: `local` arg, the full path to the config
-/// file (`.ci.yaml`), and the full pathto the `TESTOWNERS` file.
-Future<void> main(List<String> args) async {
- if (args.length != 2 && args.length != 3) {
- print('validate_task_ownership.dart \$repo \$sha');
- print('validate_task_ownership.dart local \$local_ci_yaml \$local_TESTOWNERS');
- io.exit(1);
- }
- List<String> noOwnerBuilders;
- if (args.length == 2) {
- noOwnerBuilders = await remoteCheck(args[0], args[1]);
- } else {
- noOwnerBuilders = localCheck(args[1], args[2]);
- }
- if (noOwnerBuilders.isNotEmpty) {
- print('# Test ownership check failed.');
- print('Builders missing owner: $noOwnerBuilders');
- print('Please define ownership in https://github.com/flutter/flutter/blob/master/TESTOWNERS');
- io.exit(1);
- } else {
- print('# Test ownership check succeeded.');
- }
-}
diff --git a/app_dart/build.yaml b/app_dart/build.yaml
deleted file mode 100644
index aaf332d..0000000
--- a/app_dart/build.yaml
+++ /dev/null
@@ -1,8 +0,0 @@
-targets:
- $default:
- builders:
- source_gen|combining_builder:
- options:
- ignore_for_file:
- - always_specify_types
- - implicit_dynamic_parameter
diff --git a/app_dart/cloudbuild_app_dart.yaml b/app_dart/cloudbuild_app_dart.yaml
deleted file mode 100644
index 6f10220..0000000
--- a/app_dart/cloudbuild_app_dart.yaml
+++ /dev/null
@@ -1,40 +0,0 @@
-# Provide instructions for google Cloud Build to auto-build flutter
-# dashboard to flutter-dashboard project. Auto-build will be triggered
-# by daily schedule on `main` branch. This cloudbuild calls an additional
-# cloudbuild configuration responsible for deployment.
-#
-# This job is for generating the docker image with build provenance,
-# and the deployment job uses the generated docker image and deploys it to
-# App Engine.
-
-steps:
- # Build dashboard.
- # This step generates the dashboard files using flutter, then moves the
- # generated files into the app_dart folder, where a docker image is then
- # created in the next step.
- - name: us-docker.pkg.dev/$PROJECT_ID/flutter/flutter
- entrypoint: '/bin/bash'
- args: ['cloud_build/dashboard_build.sh']
-
- # Build docker image
- - name: 'us-docker.pkg.dev/cloud-builders/ga/v1/docker'
- args: ['build', '-t', 'us-docker.pkg.dev/$PROJECT_ID/appengine/default.version-$SHORT_SHA', 'app_dart']
-
- # Trigger the cloud build that deploys the docker image
- - name: gcr.io/cloud-builders/gcloud
- entrypoint: '/bin/bash'
- args:
- - '-c'
- - |-
- gcloud builds submit \
- --config app_dart/cloudbuild_app_dart_deploy.yaml \
- --substitutions="SHORT_SHA=$SHORT_SHA" \
- --async
-
-timeout: 1200s
-
-images: ['us-docker.pkg.dev/$PROJECT_ID/appengine/default.version-$SHORT_SHA']
-
-# If build provenance is not generated, the docker deployment will fail.
-options:
- requestedVerifyOption: VERIFIED
diff --git a/app_dart/cloudbuild_app_dart_deploy.yaml b/app_dart/cloudbuild_app_dart_deploy.yaml
deleted file mode 100644
index ab31d39..0000000
--- a/app_dart/cloudbuild_app_dart_deploy.yaml
+++ /dev/null
@@ -1,41 +0,0 @@
-# Provide instructions for google Cloud Build to auto-build flutter
-# dashboard to flutter-dashboard project. Auto-build will be triggered
-# by daily schedule on `main` branch.
-#
-# The auto-build will be skipped if no new commits since last deployment.
-
-steps:
- # Get recently pushed docker image and associated provenance, along with the
- # correct docker digest url, including the hash.
- - name: gcr.io/cloud-builders/gcloud
- entrypoint: '/bin/bash'
- args:
- - '-c'
- - |-
- cloud_build/get_docker_image_provenance.sh \
- us-docker.pkg.dev/$PROJECT_ID/appengine/default.version-$SHORT_SHA:latest \
- unverified_provenance.json
-
- # Verify provenance is valid before proceeding with deployment.
- - name: 'golang:1.20'
- entrypoint: '/bin/bash'
- args:
- - '-c'
- - |-
- cloud_build/verify_provenance.sh unverified_provenance.json
-
- # Deploy a new version to google cloud.
- - name: gcr.io/cloud-builders/gcloud
- entrypoint: '/bin/bash'
- args:
- - '-c'
- - |-
- gcloud config set project $PROJECT_ID
- latest_version=$(gcloud app versions list --hide-no-traffic --format 'value(version.id)')
- if [ "$latest_version" = "version-$SHORT_SHA" ]; then
- echo "No updates since last deployment."
- else
- bash cloud_build/deploy_app_dart.sh $PROJECT_ID $SHORT_SHA
- fi
-
-timeout: 1200s
diff --git a/app_dart/dev/deploy.dart b/app_dart/dev/deploy.dart
deleted file mode 100644
index 6e74186..0000000
--- a/app_dart/dev/deploy.dart
+++ /dev/null
@@ -1,155 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-import 'dart:io';
-
-import 'package:args/args.dart';
-
-const String cloudbuildDirectory = 'cloud_build';
-const String workspaceDirectory = '../';
-
-const String gcloudProjectIdFlag = 'project';
-const String gcloudProjectIdAbbrFlag = 'p';
-
-const String gcloudProjectVersionFlag = 'version';
-const String gcloudProjectVersionAbbrFlag = 'v';
-
-const String ignoreVersionFlag = 'ignore-version-check';
-const String helpFlag = 'help';
-
-String? _gcloudProjectId;
-String? _gcloudProjectVersion;
-
-late bool _ignoreVersion;
-
-/// Check if [gcloudProjectIdFlag] and [gcloudProjectVersionFlag]
-/// were passed as arguments. If they were, also set [_gcloudProjectId]
-/// and [_gcloudProjectVersion] accordingly.
-bool _getArgs(ArgParser argParser, List<String> arguments) {
- final ArgResults args = argParser.parse(arguments);
-
- final bool printHelpMessage = args[helpFlag] as bool;
- if (printHelpMessage) {
- return false;
- }
-
- _gcloudProjectId = args[gcloudProjectIdFlag] as String?;
- _gcloudProjectVersion = args[gcloudProjectVersionFlag] as String?;
- _ignoreVersion = args[ignoreVersionFlag] as bool;
-
- if (_gcloudProjectId == null) {
- stderr.write('--$gcloudProjectIdFlag must be defined\n');
- return false;
- }
-
- if (_gcloudProjectVersion == null) {
- stderr.write('--$gcloudProjectVersionFlag must be defined\n');
- return false;
- }
-
- return true;
-}
-
-/// Check the Flutter version installed and make sure it is a recent version
-/// from the past 21 days.
-///
-/// Flutter tools handles the rest of the checks (e.g. Dart version) when
-/// building the project.
-Future<bool> _checkDependencies() async {
- if (_ignoreVersion) {
- return true;
- }
-
- stdout.writeln('Checking Flutter version via flutter --version');
- final ProcessResult result = await Process.run('flutter', <String>['--version']);
- final String flutterVersionOutput = result.stdout as String;
-
- // This makes an assumption that only the framework will have its version
- // printed out with the date in YYYY-MM-DD format.
- final RegExp dateRegExp = RegExp(r'([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))');
- final String flutterVersionDateRaw = dateRegExp.allMatches(flutterVersionOutput).first.group(0)!;
-
- final DateTime flutterVersionDate = DateTime.parse(flutterVersionDateRaw);
- final DateTime now = DateTime.now();
- final Duration lastUpdateToFlutter = now.difference(flutterVersionDate);
-
- return lastUpdateToFlutter.inDays < 21;
-}
-
-/// Run the Google Cloud CLI tool to deploy to [_gcloudProjectId] under
-/// version [_gcloudProjectVersion].
-Future<bool> _deployToAppEngine() async {
- stdout.writeln('Deploying to AppEngine');
-
- /// The Google Cloud deployment command is an interactive process. It will
- /// print out what it is about to do, and ask for confirmation (Y/n).
- final Process process = await Process.start(
- 'gcloud',
- <String>[
- 'app',
- 'deploy',
- '--project',
- _gcloudProjectId!,
- '--version',
- _gcloudProjectVersion!,
- '--no-promote',
- '--no-stop-previous-version',
- '--quiet',
- ],
- );
-
- /// Let this user confirm the details before Google Cloud sends for deployment.
- unawaited(stdin.pipe(process.stdin));
-
- await process.stderr.pipe(stderr);
- await process.stdout.pipe(stdout);
-
- return await process.exitCode == 0;
-}
-
-/// Run [args] in bash shell and validate it finshes with exit code 0.
-Future<void> shellCommand(List<String> args) async {
- final ProcessResult result = await Process.run(
- 'bash',
- args,
- workingDirectory: workspaceDirectory,
- );
-
- if (result.exitCode != 0) {
- print('$args failed with exit code ${result.exitCode}');
- print('stdout: ${result.stdout}');
- print('stderr: ${result.stderr}');
- exit(1);
- }
-}
-
-Future<void> main(List<String> arguments) async {
- final ArgParser argParser = ArgParser()
- ..addOption(gcloudProjectIdFlag, abbr: gcloudProjectIdAbbrFlag)
- ..addOption(gcloudProjectVersionFlag, abbr: gcloudProjectVersionAbbrFlag)
- ..addFlag(ignoreVersionFlag)
- ..addFlag(helpFlag);
-
- if (!_getArgs(argParser, arguments)) {
- stdout.write('Required flags:\n'
- '--$gcloudProjectIdFlag gcp-id\n'
- '--$gcloudProjectVersionFlag version\n\n'
- 'Optional flags:\n'
- '--$ignoreVersionFlag\tForce deploy with current Flutter version\n');
- exit(1);
- }
-
- if (!await _checkDependencies()) {
- stderr.writeln('Update Flutter to a version on master from the past 3 weeks to deploy Cocoon');
- exit(1);
- }
-
- await shellCommand(<String>['$cloudbuildDirectory/dashboard_build.sh']);
-
- if (!await _deployToAppEngine()) {
- stderr.writeln('Failed to deploy to AppEngine');
- exit(1);
- }
-}
diff --git a/app_dart/index.yaml b/app_dart/index.yaml
deleted file mode 100644
index b036efd..0000000
--- a/app_dart/index.yaml
+++ /dev/null
@@ -1,48 +0,0 @@
-# Copyright (c) 2016 The Flutter Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-indexes:
-- kind: Checklist
- ancestor: yes
- properties:
- - name: CreateTimestamp
- direction: desc
-- kind: Checklist
- properties:
- - name: Branch
- - name: CreateTimestamp
- direction: desc
-- kind: Checklist
- properties:
- - name: FlutterRepositoryPath
- - name: Branch
- - name: CreateTimestamp
- direction: desc
-- kind: Task
- ancestor: yes
- properties:
- - name: StageName
- direction: desc
-- kind: Task
- properties:
- - name: Status
- - name: CreateTimestamp
- direction: desc
-- kind: Task
- ancestor: yes
- properties:
- - name: Name
- - name: CreateTimestamp
- direction: desc
-- kind: Task
- ancestor: yes
- properties:
- - name: CreateTimestamp
- direction: desc
-- kind: LogChunk
- ancestor: no
- properties:
- - name: OwnerKey
- - name: CreateTimestamp
- direction: asc
diff --git a/app_dart/integration_test/common.dart b/app_dart/integration_test/common.dart
deleted file mode 100644
index a266851..0000000
--- a/app_dart/integration_test/common.dart
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2021 The Flutter 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:github/github.dart';
-import 'package:test/test.dart';
-
-/// Validates the local tree has no outstanding changes.
-void expectNoDiff(String path) {
- final ProcessResult gitResult = Process.runSync('git', <String>['diff', '--exit-code', path]);
- if (gitResult.exitCode != 0) {
- final ProcessResult gitDiffOutput = Process.runSync('git', <String>['diff', path]);
- fail('The working tree has a diff. Ensure changes are checked in:\n${gitDiffOutput.stdout}');
- }
-}
-
-/// Wrapper class to make it easy to add new repos + branches to the validation suite.
-class SupportedConfig {
- SupportedConfig(this.slug, [this.branch = 'master']);
-
- final RepositorySlug slug;
- final String branch;
-
- @override
- String toString() => '${slug.fullName}/$branch';
-}
diff --git a/app_dart/integration_test/data/cocoon_config.json b/app_dart/integration_test/data/cocoon_config.json
deleted file mode 100644
index 8186011..0000000
--- a/app_dart/integration_test/data/cocoon_config.json
+++ /dev/null
@@ -1 +0,0 @@
-{"targets":[{"name":"Linux Cocoon","properties":{"add_recipes_cq":"true"},"runIf":[".ci.yaml","analyze/**","app_dart/**","auto_submit/**","cipd_packages/**","cloud_build/**","dashboard/**","dev/**","licenses/**","packages/**","test_utilities/**","tooling/**"],"recipe":"cocoon/cocoon"},{"name":"Linux device_doctor","properties":{"script":"cipd_packages/device_doctor/tool/build.sh","cipd_name":"flutter/device_doctor/linux-amd64"},"runIf":["cipd_packages/device_doctor/**",".ci.yaml"],"recipe":"cocoon/cipd"},{"name":"Mac device_doctor","properties":{"script":"cipd_packages/device_doctor/tool/build.sh","cipd_name":"flutter/device_doctor/mac-amd64","device_type":"none"},"runIf":["cipd_packages/device_doctor/**",".ci.yaml"],"recipe":"cocoon/cipd"},{"name":"Mac_arm64 device_doctor","properties":{"script":"cipd_packages/device_doctor/tool/build.sh","cipd_name":"flutter/device_doctor/mac-arm64","device_type":"none"},"runIf":["cipd_packages/device_doctor/**",".ci.yaml"],"recipe":"cocoon/cipd"},{"name":"Windows device_doctor","properties":{"script":"cipd_packages\\device_doctor\\tool\\build.bat","cipd_name":"flutter/device_doctor/windows-amd64"},"runIf":["cipd_packages/device_doctor/**",".ci.yaml"],"recipe":"cocoon/cipd"},{"name":"Linux doxygen","properties":{"script":"cipd_packages/doxygen/tool/build.sh","cipd_name":"flutter/doxygen/linux-amd64","dependencies":"[\n {\"dependency\": \"cmake\", \"version\": \"build_id:8787856497187628321\"}\n]"},"runIf":["cipd_packages/doxygen/**",".ci.yaml"],"recipe":"cocoon/cipd"},{"name":"Mac codesign","properties":{"script":"cipd_packages/codesign/tool/build.sh","cipd_name":"flutter/codesign/mac-amd64","device_type":"none"},"runIf":["cipd_packages/codesign/**",".ci.yaml"],"recipe":"cocoon/cipd"},{"name":"Mac_arm64 codesign","properties":{"script":"cipd_packages/codesign/tool/build.sh","cipd_name":"flutter/codesign/mac-arm64","device_type":"none"},"runIf":["cipd_packages/codesign/**",".ci.yaml"],"recipe":"cocoon/cipd"},{"name":"Mac ruby","timeout":60,"properties":{"script":"cipd_packages/ruby/tools/build.sh","cipd_name":"flutter/ruby/mac-amd64","device_os":"iOS","contexts":"[\n \"osx_sdk_devicelab\"\n]","$flutter/osx_sdk":"{\n \"sdk_version\": \"14e300c\"\n}"},"runIf":["cipd_packages/ruby/**",".ci.yaml"],"recipe":"cocoon/cipd"},{"name":"Mac_arm64 ruby","timeout":60,"properties":{"script":"cipd_packages/ruby/tools/build.sh","cipd_name":"flutter/ruby/mac-arm64","device_os":"iOS","contexts":"[\n \"osx_sdk_devicelab\"\n]","$flutter/osx_sdk":"{\n \"sdk_version\": \"14e300c\"\n}"},"runIf":["cipd_packages/ruby/**",".ci.yaml"],"recipe":"cocoon/cipd"},{"name":"Linux ci_yaml roller","properties":{"backfill":"false"},"runIf":[".ci.yaml"],"recipe":"infra/ci_yaml"}],"enabledBranches":["main"],"platformProperties":{"linux":{"properties":{"os":"Linux","device_type":"none"}},"mac":{"properties":{"os":"Mac-12|Mac-13","cpu":"x86"}},"mac_arm64":{"properties":{"os":"Mac-12|Mac-13","cpu":"arm64"}},"windows":{"properties":{"os":"Windows","device_type":"none"}}}}
diff --git a/app_dart/integration_test/generate_jspb_test.dart b/app_dart/integration_test/generate_jspb_test.dart
deleted file mode 100644
index 2938181..0000000
--- a/app_dart/integration_test/generate_jspb_test.dart
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2021 The Flutter 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:test/test.dart';
-
-import 'common.dart';
-
-/// Validates `bin/generate_jspb.dart` which is used in the ci.yaml roller script.
-Future<void> main() async {
- test('validate cocoon ci.yaml generates jspb', () async {
- final ProcessResult generateResult =
- Process.runSync('dart', <String>['run', 'bin/generate_jspb.dart', '../.ci.yaml']);
- if (generateResult.exitCode != 0) {
- fail('generate_jspb.dart failed with exit code ${generateResult.exitCode}\n'
- 'stderr: ${generateResult.stderr}\n'
- 'stdout: ${generateResult.stdout}');
- }
-
- // Update expectations file
- final File jspbExpectationsFile = File('integration_test/data/cocoon_config.json');
- jspbExpectationsFile.writeAsStringSync(generateResult.stdout as String);
-
- expectNoDiff(jspbExpectationsFile.path);
- });
-}
diff --git a/app_dart/integration_test/validate_all_ci_configs_test.dart b/app_dart/integration_test/validate_all_ci_configs_test.dart
deleted file mode 100644
index 2032f56..0000000
--- a/app_dart/integration_test/validate_all_ci_configs_test.dart
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright 2021 The Flutter 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:core';
-import 'dart:io';
-
-import 'package:cocoon_service/cocoon_service.dart';
-import 'package:cocoon_service/src/model/ci_yaml/ci_yaml.dart';
-import 'package:cocoon_service/src/model/proto/internal/scheduler.pbserver.dart' as pb;
-import 'package:github/github.dart';
-import 'package:http/http.dart' as http;
-import 'package:process/process.dart';
-import 'package:test/test.dart';
-import 'package:yaml/yaml.dart';
-
-import 'common.dart';
-
-/// List of repositories that have supported .ci.yaml config files.
-final List<SupportedConfig> configs = <SupportedConfig>[
- SupportedConfig(RepositorySlug('flutter', 'cocoon'), 'main'),
- SupportedConfig(RepositorySlug('flutter', 'engine'), 'main'),
- SupportedConfig(RepositorySlug('flutter', 'flutter')),
- SupportedConfig(RepositorySlug('flutter', 'packages'), 'main'),
-];
-
-Future<void> main() async {
- for (final SupportedConfig config in configs) {
- test('validate config file of $config', () async {
- final String configContent = await githubFileContent(
- config.slug,
- kCiYamlPath,
- httpClientProvider: () => http.Client(),
- ref: config.branch,
- );
- final YamlMap configYaml = loadYaml(configContent) as YamlMap;
- final pb.SchedulerConfig currentSchedulerConfig = pb.SchedulerConfig()..mergeFromProto3Json(configYaml);
- try {
- CiYaml(
- slug: config.slug,
- branch: Config.defaultBranch(config.slug),
- config: currentSchedulerConfig,
- );
- } on FormatException catch (e) {
- fail(e.message);
- }
- });
-
- test(
- 'validate enabled branches of $config',
- () async {
- final String configContent = await githubFileContent(
- config.slug,
- kCiYamlPath,
- httpClientProvider: () => http.Client(),
- ref: config.branch,
- );
- final YamlMap configYaml = loadYaml(configContent) as YamlMap;
- final pb.SchedulerConfig schedulerConfig = pb.SchedulerConfig()..mergeFromProto3Json(configYaml);
-
- final List<String> githubBranches = getBranchesForRepository(config.slug);
-
- final Map<String, bool> validEnabledBranches = <String, bool>{};
- // Add config wide enabled branches
- for (String enabledBranch in schedulerConfig.enabledBranches) {
- validEnabledBranches[enabledBranch] = false;
- }
- // Add all target specific enabled branches
- for (pb.Target target in schedulerConfig.targets) {
- for (String enabledBranch in target.enabledBranches) {
- validEnabledBranches[enabledBranch] = false;
- }
- }
-
- // N^2 scan to verify all enabled branch patterns match an exist branch on the repo.
- for (String enabledBranch in validEnabledBranches.keys) {
- for (String githubBranch in githubBranches) {
- if (CiYaml.enabledBranchesMatchesCurrentBranch(<String>[enabledBranch], githubBranch)) {
- validEnabledBranches[enabledBranch] = true;
- }
- }
- }
-
- if (config.slug.name == 'engine') {
- print(githubBranches);
- print(validEnabledBranches);
- }
-
- // Verify the enabled branches
- for (String enabledBranch in validEnabledBranches.keys) {
- expect(
- validEnabledBranches[enabledBranch],
- isTrue,
- reason: '$enabledBranch does not match to a branch in ${config.slug.fullName}',
- );
- }
- },
- skip: config.slug.name == 'flutter',
- );
- }
-}
-
-/// Gets all branches for [slug].
-///
-/// Internally, uses the git on path to get the branches from the remote for [slug].
-List<String> getBranchesForRepository(RepositorySlug slug) {
- const ProcessManager processManager = LocalProcessManager();
- final ProcessResult result =
- processManager.runSync(<String>['git', 'ls-remote', '--head', 'https://github.com/${slug.fullName}']);
- final List<String> lines = (result.stdout as String).split('\n');
-
- final List<String> githubBranches = <String>[];
- for (String line in lines) {
- if (line.isEmpty) {
- continue;
- }
- // Lines follow the format of `$sha\t$ref`
- final String ref = line.split('\t')[1];
- final String branch = ref.replaceAll('refs/heads/', '');
- githubBranches.add(branch);
- }
-
- return githubBranches;
-}
diff --git a/app_dart/integration_test/validate_test_ownership_test.dart b/app_dart/integration_test/validate_test_ownership_test.dart
deleted file mode 100644
index 6710c62..0000000
--- a/app_dart/integration_test/validate_test_ownership_test.dart
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2021 The Flutter 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:async';
-import 'dart:convert';
-import 'dart:io';
-
-import 'package:github/github.dart';
-import 'package:process/process.dart';
-import 'package:test/test.dart';
-
-import 'common.dart';
-
-/// List of supported repositories with TESTOWNERS.
-final List<SupportedConfig> configs = <SupportedConfig>[
- SupportedConfig(RepositorySlug('flutter', 'flutter')),
-];
-
-Future<void> main() async {
- for (final SupportedConfig config in configs) {
- test('validate test ownership for $config', () async {
- const String dart = 'dart';
- const String taskExecutable = 'bin/validate_task_ownership.dart';
- final List<String> taskArgs = <String>[config.slug.name, config.branch];
-
- const ProcessManager processManager = LocalProcessManager();
- final Process process = await processManager.start(
- <String>[dart, taskExecutable, ...taskArgs],
- workingDirectory: Directory.current.path,
- );
-
- final List<String> output = <String>[];
- final List<String> error = <String>[];
-
- process.stdout
- .transform<String>(const Utf8Decoder())
- .transform<String>(const LineSplitter())
- .listen((String line) {
- stdout.writeln('[STDOUT] $line');
- output.add(line);
- });
-
- process.stderr
- .transform<String>(const Utf8Decoder())
- .transform<String>(const LineSplitter())
- .listen((String line) {
- stderr.writeln('[STDERR] $line');
- error.add(line);
- });
-
- final int exitCode = await process.exitCode;
- if (exitCode != 0) {
- for (String line in error) {
- print(line);
- }
- fail('An error has occurred.');
- }
- });
- }
-}
diff --git a/app_dart/lib/ci_yaml.dart b/app_dart/lib/ci_yaml.dart
deleted file mode 100644
index 756415a..0000000
--- a/app_dart/lib/ci_yaml.dart
+++ /dev/null
@@ -1,6 +0,0 @@
-// Copyright 2019 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-export 'src/model/ci_yaml/ci_yaml.dart';
-export 'src/model/ci_yaml/target.dart';
diff --git a/app_dart/lib/cocoon_service.dart b/app_dart/lib/cocoon_service.dart
deleted file mode 100644
index d0137b3..0000000
--- a/app_dart/lib/cocoon_service.dart
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2019 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-export 'src/foundation/utils.dart';
-export 'src/model/appengine/service_account_info.dart';
-export 'src/request_handlers/check_flaky_builders.dart';
-export 'src/request_handlers/create_branch.dart';
-export 'src/request_handlers/dart_internal_subscription.dart';
-export 'src/request_handlers/file_flaky_issue_and_pr.dart';
-export 'src/request_handlers/flush_cache.dart';
-export 'src/request_handlers/get_build_status.dart';
-export 'src/request_handlers/get_release_branches.dart';
-export 'src/request_handlers/get_repos.dart';
-export 'src/request_handlers/get_status.dart';
-export 'src/request_handlers/get_green_commits.dart';
-export 'src/request_handlers/github_rate_limit_status.dart';
-export 'src/request_handlers/github_webhook.dart';
-export 'src/request_handlers/github/webhook_subscription.dart';
-export 'src/request_handlers/postsubmit_luci_subscription.dart';
-export 'src/request_handlers/presubmit_luci_subscription.dart';
-export 'src/request_handlers/push_build_status_to_github.dart';
-export 'src/request_handlers/push_gold_status_to_github.dart';
-export 'src/request_handlers/readiness_check.dart';
-export 'src/request_handlers/reset_prod_task.dart';
-export 'src/request_handlers/reset_try_task.dart';
-export 'src/request_handlers/scheduler/batch_backfiller.dart';
-export 'src/request_handlers/scheduler/request_subscription.dart';
-export 'src/request_handlers/scheduler/vacuum_stale_tasks.dart';
-export 'src/request_handlers/update_existing_flaky_issues.dart';
-export 'src/request_handlers/update_task_status.dart';
-export 'src/request_handlers/vacuum_github_commits.dart';
-export 'src/request_handling/authentication.dart';
-export 'src/request_handling/body.dart';
-export 'src/request_handling/cache_request_handler.dart';
-export 'src/request_handling/pubsub.dart';
-export 'src/request_handling/pubsub_authentication.dart';
-export 'src/request_handling/request_handler.dart';
-export 'src/request_handling/static_file_handler.dart';
-export 'src/request_handling/swarming_authentication.dart';
-export 'src/service/access_token_provider.dart';
-export 'src/service/buildbucket.dart';
-export 'src/service/branch_service.dart';
-export 'src/service/cache_service.dart';
-export 'src/service/config.dart';
-export 'src/service/gerrit_service.dart';
-export 'src/service/github_checks_service.dart';
-export 'src/service/luci_build_service.dart';
-export 'src/service/scheduler.dart';
diff --git a/app_dart/lib/protos.dart b/app_dart/lib/protos.dart
deleted file mode 100644
index 5288e7f..0000000
--- a/app_dart/lib/protos.dart
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright 2019 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-export 'src/model/proto/protos.dart';
diff --git a/app_dart/lib/src/foundation/github_checks_util.dart b/app_dart/lib/src/foundation/github_checks_util.dart
deleted file mode 100644
index 7880c35..0000000
--- a/app_dart/lib/src/foundation/github_checks_util.dart
+++ /dev/null
@@ -1,170 +0,0 @@
-// Copyright 2020 The Flutter 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:core';
-import 'dart:io';
-
-import 'package:github/github.dart' as github;
-import 'package:github/hooks.dart';
-import 'package:retry/retry.dart';
-
-import '../service/config.dart';
-import '../service/logging.dart';
-
-/// Wrapper class for github checkrun service. This is used to simplify
-/// mocking during testing because some of the subclasses are private.
-class GithubChecksUtil {
- const GithubChecksUtil();
- Future<Map<String, github.CheckRun>> allCheckRuns(
- github.GitHub gitHubClient,
- CheckSuiteEvent checkSuiteEvent,
- ) async {
- final List<github.CheckRun> allCheckRuns = await gitHubClient.checks.checkRuns
- .listCheckRunsInSuite(
- checkSuiteEvent.repository!.slug(),
- checkSuiteId: checkSuiteEvent.checkSuite!.id!,
- )
- .toList();
- return {for (github.CheckRun check in allCheckRuns) check.name as String: check};
- }
-
- Future<github.CheckSuite> getCheckSuite(
- github.GitHub gitHubClient,
- github.RepositorySlug slug,
- int checkSuiteId,
- ) async {
- return gitHubClient.checks.checkSuites.getCheckSuite(
- slug,
- checkSuiteId: checkSuiteId,
- );
- }
-
- /// Finds all check suites that are associated with a given git [ref].
- Future<List<github.CheckSuite>> listCheckSuitesForRef(
- github.GitHub gitHubClient,
- github.RepositorySlug slug, {
- required String ref,
- int? appId,
- String? checkName,
- }) async {
- const RetryOptions r = RetryOptions(
- maxAttempts: 3,
- delayFactor: Duration(seconds: 2),
- );
- return r.retry(
- () async {
- return gitHubClient.checks.checkSuites
- .listCheckSuitesForRef(
- slug,
- ref: ref,
- appId: appId,
- checkName: checkName,
- )
- .toList();
- },
- retryIf: (Exception e) => e is github.GitHubError || e is SocketException,
- );
- }
-
- /// Sends a request to github checks api to update a [CheckRun] with a given
- /// [status] and [conclusion].
- Future<void> updateCheckRun(
- Config config,
- github.RepositorySlug slug,
- github.CheckRun checkRun, {
- github.CheckRunStatus status = github.CheckRunStatus.queued,
- github.CheckRunConclusion? conclusion,
- String? detailsUrl,
- github.CheckRunOutput? output,
- }) async {
- const RetryOptions r = RetryOptions(
- maxAttempts: 3,
- delayFactor: Duration(seconds: 2),
- );
- return r.retry(
- () async {
- final github.GitHub gitHubClient = await config.createGitHubClient(slug: slug);
- await gitHubClient.checks.checkRuns.updateCheckRun(
- slug,
- checkRun,
- status: status,
- conclusion: conclusion,
- detailsUrl: detailsUrl,
- output: output,
- );
- },
- retryIf: (Exception e) => e is github.GitHubError || e is SocketException,
- );
- }
-
- Future<github.CheckRun> getCheckRun(
- Config config,
- github.RepositorySlug slug,
- int? id,
- ) async {
- const RetryOptions r = RetryOptions(
- maxAttempts: 3,
- delayFactor: Duration(seconds: 2),
- );
- return r.retry(
- () async {
- final github.GitHub gitHubClient = await config.createGitHubClient(slug: slug);
- return gitHubClient.checks.checkRuns.getCheckRun(
- slug,
- checkRunId: id!,
- );
- },
- retryIf: (Exception e) => e is github.GitHubError || e is SocketException,
- );
- }
-
- /// Sends a request to GitHub's Checks API to create a new [github.CheckRun].
- ///
- /// The newly created checkrun will be associated in [slug] to [sha] as [name].
- ///
- /// Optionally, will have [output] to give information to users.
- Future<github.CheckRun> createCheckRun(
- Config config,
- github.RepositorySlug slug,
- String sha,
- String name, {
- github.CheckRunOutput? output,
- }) async {
- const RetryOptions r = RetryOptions(
- maxAttempts: 3,
- delayFactor: Duration(seconds: 2),
- );
- return r.retry(
- () async {
- return _createCheckRun(
- config,
- slug,
- sha,
- name,
- output: output,
- );
- },
- retryIf: (Exception e) => true,
- onRetry: (Exception e) => log.warning(
- 'createCheckRun fails for slug: ${slug.fullName}, sha: $sha, name: $name. Exception: ${e.toString()}',
- ),
- );
- }
-
- Future<github.CheckRun> _createCheckRun(
- Config config,
- github.RepositorySlug slug,
- String sha,
- String name, {
- github.CheckRunOutput? output,
- }) async {
- final github.GitHub gitHubClient = await config.createGitHubClient(slug: slug);
- return gitHubClient.checks.checkRuns.createCheckRun(
- slug,
- name: name,
- headSha: sha,
- output: output,
- );
- }
-}
diff --git a/app_dart/lib/src/foundation/providers.dart b/app_dart/lib/src/foundation/providers.dart
deleted file mode 100644
index 3c28318..0000000
--- a/app_dart/lib/src/foundation/providers.dart
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:appengine/appengine.dart' as gae;
-import 'package:http/http.dart' as http;
-
-import 'typedefs.dart';
-
-/// Class that holds static default providers.
-class Providers {
- const Providers._();
-
- /// Default [http.Client] provider.
- ///
- /// See also:
- ///
- /// * [HttpClientProvider], which defines this interface.
- static http.Client freshHttpClient() => http.Client();
-
- /// Default [gae.Logging] provider.
- ///
- /// See also:
- ///
- /// * [LoggingProvider], which defines this interface.
- static gae.Logging serviceScopeLogger() => gae.loggingService;
-
- /// Default [gae.ClientContext] provider.
- ///
- /// See also:
- ///
- /// * [ClientContextProvider], which defines this interface.
- static gae.ClientContext serviceScopeContext() => gae.context;
-}
diff --git a/app_dart/lib/src/foundation/typedefs.dart b/app_dart/lib/src/foundation/typedefs.dart
deleted file mode 100644
index fe0ddc5..0000000
--- a/app_dart/lib/src/foundation/typedefs.dart
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:appengine/appengine.dart';
-import 'package:http/http.dart' as http;
-
-/// Signature for a function that returns an App Engine [ClientContext].
-///
-/// This is used in [AuthenticationProvider] to provide the client context
-/// as part of the [AuthenticatedContext].
-typedef ClientContextProvider = ClientContext Function();
-
-/// Signature for a function that returns an [HttpClient].
-///
-/// This is used by [AuthenticationProvider] to provide the HTTP client that
-/// will be used (if necessary) to verify OAuth ID tokens (JWT tokens).
-typedef HttpClientProvider = http.Client Function();
diff --git a/app_dart/lib/src/foundation/utils.dart b/app_dart/lib/src/foundation/utils.dart
deleted file mode 100644
index 346ccd7..0000000
--- a/app_dart/lib/src/foundation/utils.dart
+++ /dev/null
@@ -1,245 +0,0 @@
-// Copyright 2020 The Flutter 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:async';
-import 'dart:convert';
-import 'dart:io';
-
-import 'package:cocoon_service/cocoon_service.dart';
-import 'package:github/github.dart';
-import 'package:googleapis/bigquery/v2.dart';
-import 'package:http/http.dart' as http;
-import 'package:retry/retry.dart';
-import 'package:yaml/yaml.dart';
-
-import '../../protos.dart' as pb;
-import '../foundation/typedefs.dart';
-import '../model/ci_yaml/ci_yaml.dart';
-import '../model/ci_yaml/target.dart';
-import '../request_handlers/flaky_handler_utils.dart';
-import '../request_handling/exceptions.dart';
-import '../service/logging.dart';
-
-const String kCiYamlPath = '.ci.yaml';
-const String kTestOwnerPath = 'TESTOWNERS';
-
-/// Signature for a function that calculates the backoff duration to wait in
-/// between requests when GitHub responds with an error.
-///
-/// The [attempt] argument is zero-based, so if the first attempt to request
-/// from GitHub fails, and we're backing off before making the second attempt,
-/// the [attempt] argument will be zero.
-typedef GitHubBackoffCalculator = Duration Function(int attempt);
-
-/// Default backoff calculator.
-Duration twoSecondLinearBackoff(int attempt) {
- return const Duration(seconds: 2) * (attempt + 1);
-}
-
-/// Get content of [filePath] from GitHub CDN.
-Future<String> githubFileContent(
- RepositorySlug slug,
- String filePath, {
- required HttpClientProvider httpClientProvider,
- String ref = 'master',
- Duration timeout = const Duration(seconds: 5),
- RetryOptions retryOptions = const RetryOptions(
- maxAttempts: 3,
- delayFactor: Duration(seconds: 3),
- ),
-}) async {
- final Uri githubUrl = Uri.https('raw.githubusercontent.com', '${slug.fullName}/$ref/$filePath');
- // git-on-borg has a different path for shas and refs to github
- final String gobRef = (ref.length < 40) ? 'refs/heads/$ref' : ref;
- final Uri gobUrl = Uri.https(
- 'flutter.googlesource.com',
- 'mirrors/${slug.name}/+/$gobRef/$filePath',
- <String, String>{
- 'format': 'text',
- },
- );
- late String content;
- try {
- await retryOptions.retry(
- () async => content = await getUrl(githubUrl, httpClientProvider, timeout: timeout),
- retryIf: (Exception e) => e is HttpException || e is NotFoundException,
- );
- } catch (e) {
- await retryOptions.retry(
- () async =>
- content = String.fromCharCodes(base64Decode(await getUrl(gobUrl, httpClientProvider, timeout: timeout))),
- retryIf: (Exception e) => e is HttpException,
- );
- }
- return content;
-}
-
-/// Return [String] of response from [url] if status is [HttpStatus.ok].
-///
-/// If [url] returns [HttpStatus.notFound] throw [NotFoundException].
-/// Otherwise, throws [HttpException].
-FutureOr<String> getUrl(
- Uri url,
- HttpClientProvider httpClientProvider, {
- Duration timeout = const Duration(seconds: 5),
-}) async {
- log.info('Making HTTP GET request for $url');
- final http.Client client = httpClientProvider();
- try {
- final http.Response response = await client.get(url).timeout(timeout);
-
- if (response.statusCode == HttpStatus.ok) {
- return response.body;
- } else if (response.statusCode == HttpStatus.notFound) {
- throw NotFoundException('HTTP ${response.statusCode}: $url');
- } else {
- log.warning('HTTP ${response.statusCode}: $url');
- throw HttpException('HTTP ${response.statusCode}: $url');
- }
- } finally {
- client.close();
- }
-}
-
-/// Expands globs string to a regex for evaluation.
-Future<RegExp> parseGlob(String glob) async {
- glob = glob.replaceAll('**', '[A-Za-z0-9_/.]+');
- glob = glob.replaceAll('*', '[A-Za-z0-9_.]+');
- return RegExp('^$glob\$');
-}
-
-/// Returns a LUCI [builder] list that covers changed [files].
-///
-/// [builders]: enabled luci builders.
-/// [files]: changed files in corresponding PRs.
-///
-/// [builder] format with run_if:
-/// {
-/// "name":"yyy",
-/// "repo":"flutter",
-/// "taskName":"zzz",
-/// "enabled":true,
-/// "run_if":["a/b/", "c/d_e/**", "f", "g*h/"]
-/// }
-/// [builder] format with run_if_not:
-/// {
-/// "name":"yyy",
-/// "repo":"flutter",
-/// "taskName":"zzz",
-/// "enabled":true,
-/// "run_if_not":["a/b/", "c/d_e/**", "f", "g*h/"]
-/// }
-/// Note: if both [run_if] and [run_if_not] are provided and not empty only
-/// [run_if] is evaluated.
-///
-/// [file] is based on repo root: `a/b/c.dart`.
-Future<List<Target>> getTargetsToRun(Iterable<Target> targets, List<String?> files) async {
- log.info('Getting targets to run from diff.');
- final List<Target> targetsToRun = <Target>[];
- for (Target target in targets) {
- final List<String> globs = target.value.runIf;
- // Handle case where [Target] initializes empty runif
- if (globs.isEmpty) {
- // Evaluate run_if_not.
- final List<String> negativeGlobs = target.value.runIfNot;
- if (negativeGlobs.isEmpty) {
- targetsToRun.add(target);
- continue;
- }
- bool shouldAdd = true;
- for (String glob in negativeGlobs) {
- final RegExp regExp = await parseGlob(glob);
- // if the file is not in any of the paths then add the target.
- if (files.any((String? file) => regExp.hasMatch(file!))) {
- shouldAdd = false;
- break;
- }
- }
- if (shouldAdd) {
- targetsToRun.add(target);
- }
- } else {
- for (String glob in globs) {
- // If a file is found within a pre-set dir, the builder needs to run. No need to check further.
- final RegExp regExp = await parseGlob(glob);
- if (glob.isEmpty || files.any((String? file) => regExp.hasMatch(file!))) {
- targetsToRun.add(target);
- break;
- }
- }
- }
- }
-
- log.info('Collected the following targets to run:');
- for (var target in targetsToRun) {
- log.info(target.value.name);
- }
-
- return targetsToRun;
-}
-
-Future<void> insertBigquery(String tableName, Map<String, dynamic> data, TabledataResource tabledataResourceApi) async {
- // Define const variables for [BigQuery] operations.
- const String projectId = 'flutter-dashboard';
- const String dataset = 'cocoon';
- final String table = tableName;
- final List<Map<String, Object>> requestRows = <Map<String, Object>>[];
-
- requestRows.add(<String, Object>{
- 'json': data,
- });
-
- // Obtain [rows] to be inserted to [BigQuery].
- final TableDataInsertAllRequest request = TableDataInsertAllRequest.fromJson(<String, dynamic>{'rows': requestRows});
-
- try {
- await tabledataResourceApi.insertAll(request, projectId, dataset, table);
- } on ApiRequestError catch (error) {
- log.warning('Failed to add to BigQuery: $error');
- }
-}
-
-/// Validate test ownership defined in [testOwnersContent] for tests configured in `ciYamlContent`.
-List<String> validateOwnership(String ciYamlContent, String testOwnersContent, {bool unfilteredTargets = false}) {
- final List<String> noOwnerBuilders = <String>[];
- final YamlMap? ciYaml = loadYaml(ciYamlContent) as YamlMap?;
- final pb.SchedulerConfig unCheckedSchedulerConfig = pb.SchedulerConfig()..mergeFromProto3Json(ciYaml);
-
- final CiYaml ciYamlFromProto = CiYaml(
- slug: Config.flutterSlug,
- branch: Config.defaultBranch(Config.flutterSlug),
- config: unCheckedSchedulerConfig,
- );
-
- final pb.SchedulerConfig schedulerConfig = ciYamlFromProto.config;
-
- for (pb.Target target in schedulerConfig.targets) {
- final String builder = target.name;
- final BuilderType builderType = getTypeForBuilder(
- builder,
- ciYamlFromProto,
- unfilteredTargets: unfilteredTargets,
- );
-
- final String? owner = getTestOwnership(
- target,
- builderType,
- testOwnersContent,
- ).owner;
- print('$builder: $owner');
- if (owner == null) {
- noOwnerBuilders.add(builder);
- }
- }
- return noOwnerBuilders;
-}
-
-/// Utility to class to wrap related objects in.
-class Tuple<S, T, U> {
- const Tuple(this.first, this.second, this.third);
-
- final S first;
- final T second;
- final U third;
-}
diff --git a/app_dart/lib/src/model/appengine/allowed_account.dart b/app_dart/lib/src/model/appengine/allowed_account.dart
deleted file mode 100644
index 120f129..0000000
--- a/app_dart/lib/src/model/appengine/allowed_account.dart
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:gcloud/db.dart';
-
-/// Class that represents a non-Google account that has been allowlisted to
-/// make API requests to the Flutter dashboard.
-///
-/// By default, only App Engine cronjobs, and users
-/// authenticated as "@google.com" accounts are allowed to make API requests
-/// to the Cocooon backend. This class represents instances where non-Google
-/// users have been explicitly allowlisted to make such requests.
-@Kind(name: 'AllowedAccount')
-class AllowedAccount extends Model<int> {
- /// Creates a new [AllowedAccount].
- AllowedAccount({
- Key<int>? key,
- required this.email,
- }) {
- parentKey = key?.parent;
- id = key?.id;
- }
-
- /// The email address of the account that has been allowlisted.
- @StringProperty(propertyName: 'Email', required: true)
- String email;
-
- @override
- String toString() {
- final StringBuffer buf = StringBuffer()
- ..write('$runtimeType(')
- ..write('id: $id')
- ..write(', parentKey: ${parentKey?.id}')
- ..write(', key: ${parentKey == null ? null : key.id}')
- ..write(', email: $email')
- ..write(')');
- return buf.toString();
- }
-}
diff --git a/app_dart/lib/src/model/appengine/branch.dart b/app_dart/lib/src/model/appengine/branch.dart
deleted file mode 100644
index 04d2786..0000000
--- a/app_dart/lib/src/model/appengine/branch.dart
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2021 The Flutter 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 'package:gcloud/db.dart';
-import 'package:github/github.dart';
-
-@Kind(name: 'Branch', idType: IdType.String)
-class Branch extends Model<String> {
- Branch({Key<String>? key, this.lastActivity, this.channel}) {
- parentKey = key?.parent;
- id = key?.id;
- }
-
- /// The timestamp (in milliseconds since the Epoch) of the last time
- /// when current branch had activity.
- @IntProperty(propertyName: 'lastActivity', required: false)
- int? lastActivity;
-
- /// The channel of current branch
- @StringProperty(propertyName: 'channel', required: false)
- String? channel;
-
- /// [RepositorySlug] of where this commit exists.
- RepositorySlug get slug => RepositorySlug.full(repository);
-
- String get repository => key.id!.substring(0, key.id!.lastIndexOf('/'));
-
- String get name => key.id!.substring(key.id!.lastIndexOf('/') + 1);
-
- @override
- String toString() {
- final StringBuffer buf = StringBuffer()
- ..write('$runtimeType(')
- ..write('id: $id')
- ..write(', key: ${parentKey == null ? null : key.id}')
- ..write(', branch: $name')
- ..write(', channel: $channel')
- ..write(', repository: $repository')
- ..write(', lastActivity: $lastActivity')
- ..write(')');
- return buf.toString();
- }
-
- Map<String, dynamic> toJson() {
- return <String, dynamic>{
- 'id': id,
- 'branch': <String, dynamic>{
- 'branch': name,
- 'repository': repository,
- },
- };
- }
-}
diff --git a/app_dart/lib/src/model/appengine/cocoon_config.dart b/app_dart/lib/src/model/appengine/cocoon_config.dart
deleted file mode 100644
index 25705c1..0000000
--- a/app_dart/lib/src/model/appengine/cocoon_config.dart
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:gcloud/db.dart';
-
-@Kind(name: 'CocoonConfig', idType: IdType.String)
-class CocoonConfig extends Model<String> {
- @StringProperty(propertyName: 'ParameterValue')
- late String value;
-}
diff --git a/app_dart/lib/src/model/appengine/commit.dart b/app_dart/lib/src/model/appengine/commit.dart
deleted file mode 100644
index 592934e..0000000
--- a/app_dart/lib/src/model/appengine/commit.dart
+++ /dev/null
@@ -1,144 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:gcloud/db.dart';
-import 'package:github/github.dart';
-import 'package:json_annotation/json_annotation.dart';
-
-import '../../service/datastore.dart';
-import '../../service/logging.dart';
-import 'key_converter.dart';
-
-part 'commit.g.dart';
-
-/// Class that represents a commit that has landed on the master branch of a
-/// Flutter repository.
-@Kind(name: 'Checklist', idType: IdType.String)
-class Commit extends Model<String> {
- Commit({
- Key<String>? key,
- this.sha,
- this.timestamp,
- this.author,
- this.authorAvatarUrl,
- this.message,
- this.repository,
- this.branch = 'master',
- }) {
- parentKey = key?.parent;
- id = key?.id;
- }
-
- /// Create a [Key] that can be used to lookup a [Commit] from Datastore.
- static Key<String> createKey({
- required DatastoreDB db,
- required RepositorySlug slug,
- required String gitBranch,
- required String sha,
- }) {
- return db.emptyKey.append(
- Commit,
- id: '${slug.fullName}/$gitBranch/$sha',
- );
- }
-
- /// Lookup [Commit] from Datastore.
- static Future<Commit> fromDatastore({
- required DatastoreService datastore,
- required Key<String> key,
- }) async {
- log.fine('Looking up commit by key with id: ${key.id}');
- return datastore.lookupByValue<Commit>(key);
- }
-
- /// The timestamp (in milliseconds since the Epoch) of when the commit
- /// landed.
- @IntProperty(propertyName: 'CreateTimestamp', required: true)
- int? timestamp;
-
- /// The SHA1 hash of the commit.
- @StringProperty(propertyName: 'Commit.Sha', required: true)
- String? sha;
-
- /// The GitHub username of the commit author.
- @StringProperty(propertyName: 'Commit.Author.Login')
- String? author;
-
- /// URL of the [author]'s profile image / avatar.
- ///
- /// The bytes loaded from the URL are expected to be encoded image bytes.
- @StringProperty(propertyName: 'Commit.Author.AvatarURL')
- String? authorAvatarUrl;
-
- /// The commit message.
- ///
- /// This may be null, since we didn't always load/store this property in
- /// the datastore, so historical entries won't have this information.
- @StringProperty(propertyName: 'Commit.Message', required: false)
- String? message;
-
- /// A serializable form of [slug].
- ///
- /// This will be of the form `<org>/<repo>`. e.g. `flutter/flutter`.
- @StringProperty(propertyName: 'FlutterRepositoryPath', required: true)
- String? repository;
-
- /// The branch of the commit.
- @StringProperty(propertyName: 'Branch')
- String? branch;
-
- /// [RepositorySlug] of where this commit exists.
- RepositorySlug get slug => RepositorySlug.full(repository!);
-
- @override
- String toString() {
- final StringBuffer buf = StringBuffer()
- ..write('$runtimeType(')
- ..write('id: $id')
- ..write(', parentKey: ${parentKey?.id}')
- ..write(', key: ${parentKey == null ? null : key.id}')
- ..write(', timestamp: $timestamp')
- ..write(', sha: $sha')
- ..write(', author: $author')
- ..write(', authorAvatarUrl: $authorAvatarUrl')
- ..write(', message: ${message?.split("\n").first}')
- ..write(', repository: $repository')
- ..write(', branch: $branch')
- ..write(')');
- return buf.toString();
- }
-}
-
-/// The serialized representation of a [Commit].
-// TODO(tvolkert): Directly serialize [Commit] once frontends migrate to new serialization format.
-@JsonSerializable(createFactory: false, ignoreUnannotated: true)
-class SerializableCommit {
- const SerializableCommit(this.commit);
-
- final Commit commit;
-
- @JsonKey(name: 'Key')
- @StringKeyConverter()
- Key<String>? get key => commit.key;
-
- @JsonKey(name: 'Checklist')
- Map<String, dynamic> get facade {
- return <String, dynamic>{
- 'FlutterRepositoryPath': commit.repository,
- 'CreateTimestamp': commit.timestamp,
- 'Commit': <String, dynamic>{
- 'Sha': commit.sha,
- 'Message': commit.message,
- 'Author': <String, dynamic>{
- 'Login': commit.author,
- 'avatar_url': commit.authorAvatarUrl,
- },
- },
- 'Branch': commit.branch,
- };
- }
-
- /// Serializes this object to a JSON primitive.
- Map<String, dynamic> toJson() => _$SerializableCommitToJson(this);
-}
diff --git a/app_dart/lib/src/model/appengine/commit.g.dart b/app_dart/lib/src/model/appengine/commit.g.dart
deleted file mode 100644
index 84969e7..0000000
--- a/app_dart/lib/src/model/appengine/commit.g.dart
+++ /dev/null
@@ -1,14 +0,0 @@
-// GENERATED CODE - DO NOT MODIFY BY HAND
-
-// ignore_for_file: always_specify_types, implicit_dynamic_parameter
-
-part of 'commit.dart';
-
-// **************************************************************************
-// JsonSerializableGenerator
-// **************************************************************************
-
-Map<String, dynamic> _$SerializableCommitToJson(SerializableCommit instance) => <String, dynamic>{
- 'Key': const StringKeyConverter().toJson(instance.key),
- 'Checklist': instance.facade,
- };
diff --git a/app_dart/lib/src/model/appengine/github_build_status_update.dart b/app_dart/lib/src/model/appengine/github_build_status_update.dart
deleted file mode 100644
index 63ff03c..0000000
--- a/app_dart/lib/src/model/appengine/github_build_status_update.dart
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:gcloud/db.dart';
-
-/// Class that represents an update having been posted to a GitHub PR on the
-/// status of the Flutter build.
-@Kind(name: 'GithubBuildStatusUpdate')
-class GithubBuildStatusUpdate extends Model<int> {
- GithubBuildStatusUpdate({
- Key<int>? key,
- this.repository,
- this.pr,
- this.head,
- this.status,
- this.updates,
- this.updateTimeMillis,
- }) {
- parentKey = key?.parent;
- id = key?.id;
- }
-
- static const String statusSuccess = 'success';
-
- static const String statusFailure = 'failure';
-
- @StringProperty(propertyName: 'Repository', required: true)
- String? repository;
-
- @IntProperty(propertyName: 'PR', required: true)
- int? pr;
-
- @StringProperty(propertyName: 'Head')
- String? head;
-
- @StringProperty(propertyName: 'Status', required: true)
- String? status;
-
- @IntProperty(propertyName: 'Updates', required: true)
- int? updates;
-
- /// The last time when the status is updated for the PR.
- @IntProperty(propertyName: 'UpdateTimeMillis')
- int? updateTimeMillis;
-
- @override
- String toString() {
- final StringBuffer buf = StringBuffer()
- ..write('$runtimeType(')
- ..write('id: $id')
- ..write(', parentKey: ${parentKey?.id}')
- ..write(', key: ${parentKey == null ? null : key.id}')
- ..write(', repository: $repository')
- ..write(', pr: $pr')
- ..write(', head: $head')
- ..write(', lastStatus: $status')
- ..write(', updates: $updates')
- ..write(', updateTimeMillis: $updateTimeMillis')
- ..write(')');
- return buf.toString();
- }
-}
diff --git a/app_dart/lib/src/model/appengine/github_gold_status_update.dart b/app_dart/lib/src/model/appengine/github_gold_status_update.dart
deleted file mode 100644
index 603407a..0000000
--- a/app_dart/lib/src/model/appengine/github_gold_status_update.dart
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2020 The Flutter 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 'package:gcloud/db.dart';
-
-/// Class that represents an update having been posted to a GitHub
-/// flutter/flutter PR on the status of tryjobs generated on Flutter Gold.
-@Kind(name: 'GithubGoldStatusUpdate')
-class GithubGoldStatusUpdate extends Model<int> {
- GithubGoldStatusUpdate({
- Key<int>? key,
- this.pr,
- this.head,
- this.status,
- this.description,
- this.updates,
- this.repository,
- }) {
- parentKey = key?.parent;
- id = key?.id;
- }
-
- // The flutter-gold status cannot report a `failure` status
- // due to auto-rollers. This is why we hold a `pending` status
- // when there are image changes. This provides the opportunity
- // for images to be triaged, and the auto-roller to proceed.
- // For more context, see: https://github.com/flutter/flutter/issues/48744
-
- static const String statusCompleted = 'success';
-
- static const String statusRunning = 'pending';
-
- @IntProperty(propertyName: 'PR', required: true)
- int? pr;
-
- @StringProperty(propertyName: 'Head')
- String? head;
-
- @StringProperty(propertyName: 'Status', required: true)
- String? status;
-
- @StringProperty(propertyName: 'Description', required: true)
- String? description;
-
- @IntProperty(propertyName: 'Updates', required: true)
- int? updates;
-
- @StringProperty(propertyName: 'Repository', required: true)
- String? repository;
-
- @override
- String toString() {
- final StringBuffer buf = StringBuffer()
- ..write('$runtimeType(')
- ..write('id: $id')
- ..write(', parentKey: ${parentKey?.id}')
- ..write(', key: ${parentKey == null ? null : key.id}')
- ..write(', pr: $pr')
- ..write(', head: $head')
- ..write(', lastStatus: $status')
- ..write(', description $description')
- ..write(', updates: $updates')
- ..write(', repository: $repository')
- ..write(')');
- return buf.toString();
- }
-}
diff --git a/app_dart/lib/src/model/appengine/key_converter.dart b/app_dart/lib/src/model/appengine/key_converter.dart
deleted file mode 100644
index 60aba60..0000000
--- a/app_dart/lib/src/model/appengine/key_converter.dart
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:gcloud/db.dart';
-import 'package:json_annotation/json_annotation.dart';
-
-import 'key_helper.dart';
-
-/// A converter for [Key]s encoded as strings.
-class StringKeyConverter implements JsonConverter<Key<String>?, String> {
- const StringKeyConverter();
-
- @override
- Key<String>? fromJson(String? json) =>
- (json == null || json.isEmpty) ? null : KeyHelper().decode(json) as Key<String>;
-
- @override
- String toJson(Key<String>? key) => key == null ? '' : KeyHelper().encode(key);
-}
-
-/// A converter for [Key]s encoded as strings.
-class IntKeyConverter implements JsonConverter<Key<int>, String> {
- const IntKeyConverter();
-
- @override
- Key<int> fromJson(String json) => KeyHelper().decode(json) as Key<int>;
-
- @override
- String toJson(Key<int?> key) => KeyHelper().encode(key);
-}
diff --git a/app_dart/lib/src/model/appengine/key_helper.dart b/app_dart/lib/src/model/appengine/key_helper.dart
deleted file mode 100644
index f5d6be4..0000000
--- a/app_dart/lib/src/model/appengine/key_helper.dart
+++ /dev/null
@@ -1,152 +0,0 @@
-// Copyright 2019 The Flutter 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:convert';
-import 'dart:mirrors';
-import 'dart:typed_data';
-
-import 'package:appengine/appengine.dart';
-import 'package:appengine/appengine.dart' as gae show context;
-import 'package:fixnum/fixnum.dart';
-import 'package:gcloud/db.dart';
-import 'package:meta/meta.dart';
-
-import 'allowed_account.dart';
-import 'commit.dart';
-import 'github_build_status_update.dart';
-import 'key_helper.pb.dart';
-import 'task.dart';
-
-const Set<Type> _defaultTypes = <Type>{
- Commit,
- GithubBuildStatusUpdate,
- Task,
- AllowedAccount,
-};
-
-/// Class used to encode and decode [Key] objects.
-///
-/// The encoding uses binary-encoded protocol buffers that are then base-64 URL
-/// encoded (and the decoding reverses that process).
-///
-/// This encoding scheme is necessary to match the behavior of the Go AppEngine
-/// datastore library. This parity is required while Cocoon operates with
-/// two backends, because the serialized values vended by one backend must
-/// be deserializable by the other backend.
-@immutable
-class KeyHelper {
- KeyHelper({
- AppEngineContext? applicationContext,
- Set<Type> types = _defaultTypes,
- }) : applicationContext = applicationContext ?? gae.context.applicationContext,
- types = _populateTypes(types);
-
- /// Metadata about the App Engine application.
- final AppEngineContext applicationContext;
-
- /// Maps Dart [Model] classes to their corresponding App Engine datastore
- /// type names.
- ///
- /// This is initialized when the [KeyHelper] is created by iterating over
- /// the `types` argument to the [KeyHelper.new] constructor and looking for
- /// `@`[Kind] annotations on those classes.
- final Map<Type, Kind> types;
-
- /// Encodes the specified [key] as a base-64 encoded protocol buffer
- /// representation of the key.
- ///
- /// See also:
- ///
- /// * <https://github.com/golang/appengine/blob/b2f4a3cf3c67576a2ee09e1fe62656a5086ce880/datastore/key.go#L231>
- String encode(Key<dynamic> key) {
- final Reference reference = Reference()
- ..app = applicationContext.applicationID
- ..path = _asPath(key);
- if (applicationContext.partition.isNotEmpty) {
- reference.nameSpace = applicationContext.partition;
- }
- final Uint8List buffer = reference.writeToBuffer();
- final String base64Encoded = base64Url.encode(buffer);
- return base64Encoded.split('=').first;
- }
-
- /// Decodes the specified [encoded] string into its [Key] representation.
- ///
- /// See also:
- ///
- /// * [encode], which is the complement to this method.
- /// * <https://github.com/golang/appengine/blob/b2f4a3cf3c67576a2ee09e1fe62656a5086ce880/datastore/key.go#L244>
- Key<dynamic> decode(String encoded) {
- // Re-add padding.
- final int remainder = encoded.length % 4;
- if (remainder != 0) {
- final String padding = '=' * (4 - remainder);
- encoded += padding;
- }
-
- final Uint8List decoded = base64Url.decode(encoded);
- final Reference reference = Reference.fromBuffer(decoded);
- return reference.path.element.fold<Key<dynamic>>(
- Key<int>.emptyKey(Partition(reference.nameSpace.isEmpty ? null : reference.nameSpace)),
- (Key<dynamic> previous, Path_Element element) {
- final Iterable<MapEntry<Type, Kind>> entries =
- types.entries.where((MapEntry<Type, Kind> entry) => entry.value.name == element.type);
- if (entries.isEmpty) {
- throw StateError('Unknown type: ${element.type}');
- }
- final MapEntry<Type, Kind> entry = entries.single;
- if (entry.value.idType == IdType.String) {
- return previous.append<String>(entry.key, id: element.name);
- } else {
- return previous.append<int>(entry.key, id: element.id.toInt());
- }
- },
- );
- }
-
- static Map<Type, Kind> _populateTypes(Set<Type> types) {
- final Map<Type, Kind> result = <Type, Kind>{};
-
- for (Type type in types) {
- final ClassMirror classMirror = reflectClass(type);
- final List<InstanceMirror> kindAnnotations = classMirror.metadata
- .where((InstanceMirror annotation) => annotation.hasReflectee)
- .where((InstanceMirror annotation) => annotation.reflectee.runtimeType == Kind)
- .toList();
- if (kindAnnotations.isEmpty) {
- throw StateError('Class $type has no @Kind annotation');
- }
- final Kind annotation = kindAnnotations.single.reflectee as Kind;
- result[type] = Kind(
- name: annotation.name ?? type.toString(),
- idType: annotation.idType,
- );
- }
-
- return Map<Type, Kind>.unmodifiable(result);
- }
-
- Path _asPath(Key<dynamic> key) {
- final List<Key<dynamic>> path = <Key<dynamic>>[];
- for (Key<dynamic>? current = key; current != null && !current.isEmpty; current = current.parent) {
- path.insert(0, current);
- }
- return Path()
- ..element.addAll(
- path.map<Path_Element>((Key<dynamic> key) {
- final Path_Element element = Path_Element();
- if (key.type != null) {
- element.type = types.containsKey(key.type) ? types[key.type!]!.name! : key.type.toString();
- }
- final Object? id = key.id;
- if (id is String) {
- element.name = id;
- } else if (id is int) {
- element.id = Int64(id);
- }
- return element;
- }),
- );
- }
-}
diff --git a/app_dart/lib/src/model/appengine/key_helper.pb.dart b/app_dart/lib/src/model/appengine/key_helper.pb.dart
deleted file mode 100644
index 57bc0fb..0000000
--- a/app_dart/lib/src/model/appengine/key_helper.pb.dart
+++ /dev/null
@@ -1,229 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/appengine/key_helper.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
-
-import 'dart:core' as $core;
-
-import 'package:fixnum/fixnum.dart' as $fixnum;
-import 'package:protobuf/protobuf.dart' as $pb;
-
-class Path_Element extends $pb.GeneratedMessage {
- static final $pb.BuilderInfo _i = $pb.BuilderInfo(
- const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Path.Element',
- createEmptyInstance: create)
- ..aQS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'type')
- ..aInt64(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id')
- ..aOS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name');
-
- Path_Element._() : super();
- factory Path_Element({
- $core.String? type,
- $fixnum.Int64? id,
- $core.String? name,
- }) {
- final _result = create();
- if (type != null) {
- _result.type = type;
- }
- if (id != null) {
- _result.id = id;
- }
- if (name != null) {
- _result.name = name;
- }
- return _result;
- }
- factory Path_Element.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromBuffer(i, r);
- factory Path_Element.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromJson(i, r);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
- 'Will be removed in next major version')
- Path_Element clone() => Path_Element()..mergeFromMessage(this);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
- 'Will be removed in next major version')
- Path_Element copyWith(void Function(Path_Element) updates) =>
- super.copyWith((message) => updates(message as Path_Element)) as Path_Element; // ignore: deprecated_member_use
- $pb.BuilderInfo get info_ => _i;
- @$core.pragma('dart2js:noInline')
- static Path_Element create() => Path_Element._();
- Path_Element createEmptyInstance() => create();
- static $pb.PbList<Path_Element> createRepeated() => $pb.PbList<Path_Element>();
- @$core.pragma('dart2js:noInline')
- static Path_Element getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Path_Element>(create);
- static Path_Element? _defaultInstance;
-
- @$pb.TagNumber(2)
- $core.String get type => $_getSZ(0);
- @$pb.TagNumber(2)
- set type($core.String v) {
- $_setString(0, v);
- }
-
- @$pb.TagNumber(2)
- $core.bool hasType() => $_has(0);
- @$pb.TagNumber(2)
- void clearType() => clearField(2);
-
- @$pb.TagNumber(3)
- $fixnum.Int64 get id => $_getI64(1);
- @$pb.TagNumber(3)
- set id($fixnum.Int64 v) {
- $_setInt64(1, v);
- }
-
- @$pb.TagNumber(3)
- $core.bool hasId() => $_has(1);
- @$pb.TagNumber(3)
- void clearId() => clearField(3);
-
- @$pb.TagNumber(4)
- $core.String get name => $_getSZ(2);
- @$pb.TagNumber(4)
- set name($core.String v) {
- $_setString(2, v);
- }
-
- @$pb.TagNumber(4)
- $core.bool hasName() => $_has(2);
- @$pb.TagNumber(4)
- void clearName() => clearField(4);
-}
-
-class Path extends $pb.GeneratedMessage {
- static final $pb.BuilderInfo _i = $pb.BuilderInfo(
- const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Path',
- createEmptyInstance: create)
- ..pc<Path_Element>(
- 1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'element', $pb.PbFieldType.PG,
- subBuilder: Path_Element.create)
- ..hasRequiredFields = false;
-
- Path._() : super();
- factory Path({
- $core.Iterable<Path_Element>? element,
- }) {
- final _result = create();
- if (element != null) {
- _result.element.addAll(element);
- }
- return _result;
- }
- factory Path.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromBuffer(i, r);
- factory Path.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromJson(i, r);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
- 'Will be removed in next major version')
- Path clone() => Path()..mergeFromMessage(this);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
- 'Will be removed in next major version')
- Path copyWith(void Function(Path) updates) =>
- super.copyWith((message) => updates(message as Path)) as Path; // ignore: deprecated_member_use
- $pb.BuilderInfo get info_ => _i;
- @$core.pragma('dart2js:noInline')
- static Path create() => Path._();
- Path createEmptyInstance() => create();
- static $pb.PbList<Path> createRepeated() => $pb.PbList<Path>();
- @$core.pragma('dart2js:noInline')
- static Path getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Path>(create);
- static Path? _defaultInstance;
-
- @$pb.TagNumber(1)
- $core.List<Path_Element> get element => $_getList(0);
-}
-
-class Reference extends $pb.GeneratedMessage {
- static final $pb.BuilderInfo _i = $pb.BuilderInfo(
- const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Reference',
- createEmptyInstance: create)
- ..aQS(13, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'app')
- ..aQM<Path>(14, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'path',
- subBuilder: Path.create)
- ..aOS(20, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'nameSpace');
-
- Reference._() : super();
- factory Reference({
- $core.String? app,
- Path? path,
- $core.String? nameSpace,
- }) {
- final _result = create();
- if (app != null) {
- _result.app = app;
- }
- if (path != null) {
- _result.path = path;
- }
- if (nameSpace != null) {
- _result.nameSpace = nameSpace;
- }
- return _result;
- }
- factory Reference.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromBuffer(i, r);
- factory Reference.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromJson(i, r);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
- 'Will be removed in next major version')
- Reference clone() => Reference()..mergeFromMessage(this);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
- 'Will be removed in next major version')
- Reference copyWith(void Function(Reference) updates) =>
- super.copyWith((message) => updates(message as Reference)) as Reference; // ignore: deprecated_member_use
- $pb.BuilderInfo get info_ => _i;
- @$core.pragma('dart2js:noInline')
- static Reference create() => Reference._();
- Reference createEmptyInstance() => create();
- static $pb.PbList<Reference> createRepeated() => $pb.PbList<Reference>();
- @$core.pragma('dart2js:noInline')
- static Reference getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Reference>(create);
- static Reference? _defaultInstance;
-
- @$pb.TagNumber(13)
- $core.String get app => $_getSZ(0);
- @$pb.TagNumber(13)
- set app($core.String v) {
- $_setString(0, v);
- }
-
- @$pb.TagNumber(13)
- $core.bool hasApp() => $_has(0);
- @$pb.TagNumber(13)
- void clearApp() => clearField(13);
-
- @$pb.TagNumber(14)
- Path get path => $_getN(1);
- @$pb.TagNumber(14)
- set path(Path v) {
- setField(14, v);
- }
-
- @$pb.TagNumber(14)
- $core.bool hasPath() => $_has(1);
- @$pb.TagNumber(14)
- void clearPath() => clearField(14);
- @$pb.TagNumber(14)
- Path ensurePath() => $_ensure(1);
-
- @$pb.TagNumber(20)
- $core.String get nameSpace => $_getSZ(2);
- @$pb.TagNumber(20)
- set nameSpace($core.String v) {
- $_setString(2, v);
- }
-
- @$pb.TagNumber(20)
- $core.bool hasNameSpace() => $_has(2);
- @$pb.TagNumber(20)
- void clearNameSpace() => clearField(20);
-}
diff --git a/app_dart/lib/src/model/appengine/key_helper.pbenum.dart b/app_dart/lib/src/model/appengine/key_helper.pbenum.dart
deleted file mode 100644
index eef0c46..0000000
--- a/app_dart/lib/src/model/appengine/key_helper.pbenum.dart
+++ /dev/null
@@ -1,6 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/appengine/key_helper.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields
diff --git a/app_dart/lib/src/model/appengine/key_helper.pbjson.dart b/app_dart/lib/src/model/appengine/key_helper.pbjson.dart
deleted file mode 100644
index 793f8d9..0000000
--- a/app_dart/lib/src/model/appengine/key_helper.pbjson.dart
+++ /dev/null
@@ -1,46 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/appengine/key_helper.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
-
-import 'dart:core' as $core;
-import 'dart:convert' as $convert;
-import 'dart:typed_data' as $typed_data;
-
-@$core.Deprecated('Use pathDescriptor instead')
-const Path$json = {
- '1': 'Path',
- '2': [
- {'1': 'element', '3': 1, '4': 3, '5': 10, '6': '.Path.Element', '10': 'element'},
- ],
- '3': [Path_Element$json],
-};
-
-@$core.Deprecated('Use pathDescriptor instead')
-const Path_Element$json = {
- '1': 'Element',
- '2': [
- {'1': 'type', '3': 2, '4': 2, '5': 9, '10': 'type'},
- {'1': 'id', '3': 3, '4': 1, '5': 3, '10': 'id'},
- {'1': 'name', '3': 4, '4': 1, '5': 9, '10': 'name'},
- ],
-};
-
-/// Descriptor for `Path`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List pathDescriptor = $convert.base64Decode(
- 'CgRQYXRoEicKB2VsZW1lbnQYASADKAoyDS5QYXRoLkVsZW1lbnRSB2VsZW1lbnQaQQoHRWxlbWVudBISCgR0eXBlGAIgAigJUgR0eXBlEg4KAmlkGAMgASgDUgJpZBISCgRuYW1lGAQgASgJUgRuYW1l');
-@$core.Deprecated('Use referenceDescriptor instead')
-const Reference$json = {
- '1': 'Reference',
- '2': [
- {'1': 'app', '3': 13, '4': 2, '5': 9, '10': 'app'},
- {'1': 'name_space', '3': 20, '4': 1, '5': 9, '10': 'nameSpace'},
- {'1': 'path', '3': 14, '4': 2, '5': 11, '6': '.Path', '10': 'path'},
- ],
-};
-
-/// Descriptor for `Reference`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List referenceDescriptor = $convert.base64Decode(
- 'CglSZWZlcmVuY2USEAoDYXBwGA0gAigJUgNhcHASHQoKbmFtZV9zcGFjZRgUIAEoCVIJbmFtZVNwYWNlEhkKBHBhdGgYDiACKAsyBS5QYXRoUgRwYXRo');
diff --git a/app_dart/lib/src/model/appengine/key_helper.pbserver.dart b/app_dart/lib/src/model/appengine/key_helper.pbserver.dart
deleted file mode 100644
index fd4acbd..0000000
--- a/app_dart/lib/src/model/appengine/key_helper.pbserver.dart
+++ /dev/null
@@ -1,8 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/appengine/key_helper.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,unnecessary_const,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name,return_of_invalid_type,unnecessary_this,prefer_final_fields,deprecated_member_use_from_same_package
-
-export 'key_helper.pb.dart';
diff --git a/app_dart/lib/src/model/appengine/key_helper.proto b/app_dart/lib/src/model/appengine/key_helper.proto
deleted file mode 100644
index 9a0313d..0000000
--- a/app_dart/lib/src/model/appengine/key_helper.proto
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2019 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-syntax = "proto2";
-
-package cocoon;
-
-// Message used in the serialization of [Key] objects.
-//
-// These are serialized to protocol buffers to match the behavior of the Go
-// AppEngine datastore library. This parity is required while Cocoon operates
-// with two backends, because the serialized values vended by one backend must
-// be deserializable by the other backend.
-//
-// See also:
-//
-// * <https://github.com/golang/appengine/blob/b2f4a3cf3c67576a2ee09e1fe62656a5086ce880/internal/datastore/datastore_v3.proto#L89>
-message Path {
- repeated group Element = 1 {
- required string type = 2;
- optional int64 id = 3;
- optional string name = 4;
- }
-}
-
-// Message used in the serialization of [Key] objects.
-//
-// These are serialized to protocol buffers to match the behavior of the Go
-// AppEngine datastore library. This parity is required while Cocoon operates
-// with two backends, because the serialized values vended by one backend must
-// be deserializable by the other backend.
-//
-// See also:
-//
-// * <https://github.com/golang/appengine/blob/b2f4a3cf3c67576a2ee09e1fe62656a5086ce880/internal/datastore/datastore_v3.proto#L97>
-message Reference {
- required string app = 13;
- optional string name_space = 20;
- required Path path = 14;
-}
diff --git a/app_dart/lib/src/model/appengine/key_wrapper.dart b/app_dart/lib/src/model/appengine/key_wrapper.dart
deleted file mode 100644
index c594176..0000000
--- a/app_dart/lib/src/model/appengine/key_wrapper.dart
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:fixnum/fixnum.dart';
-import 'package:gcloud/db.dart';
-
-import '../proto/protos.dart' as pb;
-
-import 'key_helper.dart';
-
-class KeyWrapper {
- const KeyWrapper(this.key);
-
- factory KeyWrapper.fromProto(pb.RootKey root) {
- Key<dynamic> result = Key<dynamic>.emptyKey(Partition(root.namespace));
- for (pb.Key key = root.child; key.hasChild(); key = key.child) {
- final Type type = _typeFromString(key.type);
- switch (key.whichId()) {
- case pb.Key_Id.uid:
- result = result.append<int>(type, id: key.uid.toInt());
- break;
- case pb.Key_Id.name:
- result = result.append<String>(type, id: key.name);
- break;
- case pb.Key_Id.notSet:
- result = result.append<dynamic>(type);
- break;
- }
- }
-
- return KeyWrapper(result);
- }
-
- final Key<dynamic> key;
-
- pb.RootKey toProto() {
- pb.Key? previous;
- for (Key<dynamic>? slice = key; slice != null; slice = key.parent) {
- final pb.Key current = pb.Key();
- if (slice.type != null) {
- current.type = slice.type.toString();
- }
- final Object? id = slice.id;
- if (id is String) {
- current.name = id;
- } else if (id is int) {
- current.uid = Int64(id);
- }
- if (previous != null) {
- current.child = previous;
- }
- previous = current;
-
- if (slice.isEmpty) {
- return pb.RootKey()
- ..namespace = slice.partition.namespace!
- ..child = previous;
- }
- }
-
- return pb.RootKey()..child = previous!;
- }
-
- static Type _typeFromString(String value) {
- final KeyHelper keyHelper = KeyHelper();
- return keyHelper.types.keys.singleWhere((Type type) => type.toString() == value);
- }
-}
diff --git a/app_dart/lib/src/model/appengine/service_account_info.dart b/app_dart/lib/src/model/appengine/service_account_info.dart
deleted file mode 100644
index 6c2d46a..0000000
--- a/app_dart/lib/src/model/appengine/service_account_info.dart
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:googleapis_auth/googleapis_auth.dart';
-import 'package:json_annotation/json_annotation.dart';
-
-part 'service_account_info.g.dart';
-
-/// Class that represents authentication information that enables callers to
-/// execute environment-specific tasks on behalf of their apps.
-///
-/// See also:
-///
-/// * <https://cloud.google.com/appengine/docs/flexible/python/service-account>
-///
-/// * `package:googleapis_auth`, which can make use of the service account
-/// information to obtain an OAuth 2.0 access token on behalf of the
-/// application.
-@JsonSerializable()
-class ServiceAccountInfo {
- /// Creates a new [ServiceAccountInfo] object.
- const ServiceAccountInfo({
- this.type,
- this.projectId,
- this.privateKeyId,
- this.privateKey,
- this.email,
- this.clientId,
- this.authUrl,
- this.tokenUrl,
- this.authCertUrl,
- this.clientCertUrl,
- });
-
- /// Create a new [ServiceAccountInfo] object from its JSON representation.
- factory ServiceAccountInfo.fromJson(Map<String, dynamic> json) => _$ServiceAccountInfoFromJson(json);
-
- @JsonKey(name: 'type')
- final String? type;
-
- @JsonKey(name: 'project_id')
- final String? projectId;
-
- @JsonKey(name: 'private_key_id')
- final String? privateKeyId;
-
- @JsonKey(name: 'private_key')
- final String? privateKey;
-
- @JsonKey(name: 'client_email')
- final String? email;
-
- @JsonKey(name: 'client_id')
- final String? clientId;
-
- @JsonKey(name: 'auth_uri')
- final String? authUrl;
-
- @JsonKey(name: 'token_uri')
- final String? tokenUrl;
-
- @JsonKey(name: 'auth_provider_x509_cert_url')
- final String? authCertUrl;
-
- @JsonKey(name: 'client_x509_cert_url')
- final String? clientCertUrl;
-
- /// Serializes this object to a JSON primitive.
- Map<String, dynamic> toJson() => _$ServiceAccountInfoToJson(this);
-
- /// Returns this object in its [ServiceAccountCredentials] form.
- ServiceAccountCredentials asServiceAccountCredentials() {
- return ServiceAccountCredentials(email!, ClientId(clientId!, null), privateKey!);
- }
-}
diff --git a/app_dart/lib/src/model/appengine/service_account_info.g.dart b/app_dart/lib/src/model/appengine/service_account_info.g.dart
deleted file mode 100644
index be7b228..0000000
--- a/app_dart/lib/src/model/appengine/service_account_info.g.dart
+++ /dev/null
@@ -1,35 +0,0 @@
-// GENERATED CODE - DO NOT MODIFY BY HAND
-
-// ignore_for_file: always_specify_types, implicit_dynamic_parameter
-
-part of 'service_account_info.dart';
-
-// **************************************************************************
-// JsonSerializableGenerator
-// **************************************************************************
-
-ServiceAccountInfo _$ServiceAccountInfoFromJson(Map<String, dynamic> json) => ServiceAccountInfo(
- type: json['type'] as String?,
- projectId: json['project_id'] as String?,
- privateKeyId: json['private_key_id'] as String?,
- privateKey: json['private_key'] as String?,
- email: json['client_email'] as String?,
- clientId: json['client_id'] as String?,
- authUrl: json['auth_uri'] as String?,
- tokenUrl: json['token_uri'] as String?,
- authCertUrl: json['auth_provider_x509_cert_url'] as String?,
- clientCertUrl: json['client_x509_cert_url'] as String?,
- );
-
-Map<String, dynamic> _$ServiceAccountInfoToJson(ServiceAccountInfo instance) => <String, dynamic>{
- 'type': instance.type,
- 'project_id': instance.projectId,
- 'private_key_id': instance.privateKeyId,
- 'private_key': instance.privateKey,
- 'client_email': instance.email,
- 'client_id': instance.clientId,
- 'auth_uri': instance.authUrl,
- 'token_uri': instance.tokenUrl,
- 'auth_provider_x509_cert_url': instance.authCertUrl,
- 'client_x509_cert_url': instance.clientCertUrl,
- };
diff --git a/app_dart/lib/src/model/appengine/stage.dart b/app_dart/lib/src/model/appengine/stage.dart
deleted file mode 100644
index c09b2ef..0000000
--- a/app_dart/lib/src/model/appengine/stage.dart
+++ /dev/null
@@ -1,159 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:json_annotation/json_annotation.dart';
-import 'package:meta/meta.dart';
-
-import 'commit.dart';
-import 'task.dart';
-
-part 'stage.g.dart';
-
-/// A group of related [Task]s run against a particular [Commit].
-///
-/// Stages are grouped by the infrastructure family that runs them, such as
-/// LUCI, DeviceLab on Linux, DeviceLab on Windows, etc.
-@immutable
-@JsonSerializable(createFactory: false, ignoreUnannotated: true)
-class Stage implements Comparable<Stage> {
- const Stage(this.name, this.commit, this.tasks, this.taskStatus);
-
- const Stage._(this.name, this.commit, this.tasks, this.taskStatus);
-
- /// The fixed ordering of the stages (by name).
- ///
- /// Unknown stages will be placed at the end of any ordering.
- static const List<String?> _order = <String?>[
- 'chromebot',
- 'devicelab',
- 'devicelab_win',
- 'devicelab_ios',
- ];
-
- /// Arbitrarily large index to represent the "end of the ordering".
- static const int _endOfList = 1000000;
-
- /// The name of the stage (e.g. 'devicelab', 'devicelab_win').
- ///
- /// This is guaranteed to be non-null.
- @JsonKey(name: 'Name')
- final String? name;
-
- /// The commit that owns this stage.
- ///
- /// All [tasks] will be run against this commit.
- final Commit? commit;
-
- /// The list of tasks in this stage.
- ///
- /// These tasks will be run against [commit]. This list is guaranteed to be
- /// non-empty.
- final List<Task> tasks;
-
- /// Representation of [tasks] used for JSON serialization.
- @JsonKey(name: 'Tasks')
- List<SerializableTask> get serializableTasks {
- return tasks.map<SerializableTask>((Task task) => SerializableTask(task)).toList();
- }
-
- /// The aggregate status, accounting for all [tasks] in this stage.
- ///
- /// The status is defined as follows:
- ///
- /// * If all tasks in this stage succeeded, then [Task.statusSucceeded]
- /// * If at least one task in this stage failed, then [Task.statusFailed]
- /// * If all tasks have the same status, then that status
- /// * Else [Task.statusInProgress]
- @JsonKey(name: 'Status')
- final String taskStatus;
-
- /// Whether this stage is managed by the Flutter device lab.
- ///
- /// Stages such as 'chromebot' are not managed by the Flutter
- /// device lab.
- bool get isManagedByDeviceLab => name!.startsWith('devicelab');
-
- @override
- int compareTo(Stage other) => _orderIndex(this).compareTo(_orderIndex(other));
-
- static int _orderIndex(Stage stage) {
- int index = _order.indexOf(stage.name);
- if (index == -1) {
- // Put unknown stages last.
- index = _endOfList;
- }
- return index;
- }
-
- /// Serializes this object to a JSON primitive.
- Map<String, dynamic> toJson() => _$StageToJson(this);
-
- @override
- String toString() {
- final StringBuffer buf = StringBuffer()
- ..write('$runtimeType(')
- ..write('name: $name')
- ..write(', commit: ${commit?.sha}')
- ..write(', tasks: ${tasks.length}')
- ..write(', taskStatus: $taskStatus')
- ..write(')');
- return buf.toString();
- }
-}
-
-/// A mutable class used to build instances of [Stage].
-class StageBuilder {
- /// The name of the stage.
- ///
- /// See also:
- /// * [Stage.name]
- String? name;
-
- /// The commit that owns the stage.
- ///
- /// See also:
- /// * [Stage.commit]
- Commit? commit;
-
- /// The tasks within the stage, run against [commit].
- ///
- /// See also:
- /// * [Stage.commit]
- List<Task> tasks = <Task>[];
-
- /// Builds a [Stage] from the information in this builder.
- ///
- /// Throws a [StateError] if [name] is null, [commit] is null, or [tasks] is
- /// empty.
- Stage build() {
- if (name == null) {
- throw StateError('Cannot build a stage with no name');
- }
- if (commit == null) {
- throw StateError('Cannot build a stage with no commit ($name)');
- }
- if (tasks.isEmpty) {
- throw StateError('Cannot build a stage with no tasks ($name)');
- }
- return Stage._(name, commit, List<Task>.unmodifiable(tasks), _taskStatus);
- }
-
- String get _taskStatus {
- assert(tasks.isNotEmpty);
- bool isSucceeded(Task task) => task.status == Task.statusSucceeded;
- bool isFailed(Task task) => task.status == Task.statusFailed;
-
- if (tasks.every(isSucceeded)) {
- return Task.statusSucceeded;
- }
-
- if (tasks.any(isFailed)) {
- return Task.statusFailed;
- }
-
- final String? commonStatus =
- tasks.map<String?>((Task task) => task.status).reduce((String? a, String? b) => a == b ? a : null);
- return commonStatus ?? Task.statusInProgress;
- }
-}
diff --git a/app_dart/lib/src/model/appengine/stage.g.dart b/app_dart/lib/src/model/appengine/stage.g.dart
deleted file mode 100644
index 09d4849..0000000
--- a/app_dart/lib/src/model/appengine/stage.g.dart
+++ /dev/null
@@ -1,15 +0,0 @@
-// GENERATED CODE - DO NOT MODIFY BY HAND
-
-// ignore_for_file: always_specify_types, implicit_dynamic_parameter
-
-part of 'stage.dart';
-
-// **************************************************************************
-// JsonSerializableGenerator
-// **************************************************************************
-
-Map<String, dynamic> _$StageToJson(Stage instance) => <String, dynamic>{
- 'Name': instance.name,
- 'Tasks': instance.serializableTasks,
- 'Status': instance.taskStatus,
- };
diff --git a/app_dart/lib/src/model/appengine/task.dart b/app_dart/lib/src/model/appengine/task.dart
deleted file mode 100644
index 3bcce62..0000000
--- a/app_dart/lib/src/model/appengine/task.dart
+++ /dev/null
@@ -1,548 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:gcloud/db.dart';
-import 'package:github/github.dart';
-import 'package:json_annotation/json_annotation.dart';
-
-import '../../request_handling/exceptions.dart';
-import '../../service/datastore.dart';
-import '../../service/logging.dart';
-import '../ci_yaml/target.dart';
-import '../luci/push_message.dart';
-import 'commit.dart';
-import 'key_converter.dart';
-
-import 'package:cocoon_service/src/model/luci/buildbucket.dart' as bb;
-part 'task.g.dart';
-
-/// Class that represents the intersection of a test at a particular [Commit].
-///
-/// Tasks are tests that have been run N (possibly zero) times against a
-/// particular commit.
-@JsonSerializable(createFactory: false, ignoreUnannotated: true)
-@Kind(name: 'Task')
-class Task extends Model<int> {
- /// Creates a new [Task].
- Task({
- Key<int>? key,
- this.commitKey,
- this.createTimestamp = 0,
- this.startTimestamp = 0,
- this.endTimestamp = 0,
- this.name,
- this.attempts = 0,
- this.isFlaky = false,
- this.isTestFlaky = false,
- this.timeoutInMinutes,
- this.reason = '',
- this.requiredCapabilities,
- this.reservedForAgentId = '',
- this.stageName,
- this.buildNumber,
- this.buildNumberList,
- this.builderName,
- this.luciBucket,
- String? status,
- }) : _status = status {
- if (status != null && !legalStatusValues.contains(status)) {
- throw ArgumentError('Invalid state: "$status"');
- }
- parentKey = key?.parent;
- id = key?.id;
- }
-
- /// Construct [Task] from a [Target].
- factory Task.fromTarget({
- required Commit commit,
- required Target target,
- }) {
- return Task(
- attempts: 1,
- builderName: target.value.name,
- commitKey: commit.key,
- createTimestamp: commit.timestamp!,
- isFlaky: target.value.bringup,
- key: commit.key.append(Task),
- name: target.value.name,
- requiredCapabilities: <String>[target.value.testbed],
- stageName: target.value.scheduler.toString(),
- status: Task.statusNew,
- timeoutInMinutes: target.value.timeout,
- );
- }
-
- /// Lookup [Task] from Datastore from its parent key and name.
- static Future<Task> fromCommitKey({
- required DatastoreService datastore,
- required Key<String> commitKey,
- required String name,
- }) async {
- if (name.isEmpty) {
- throw const BadRequestException('task name is null');
- }
- final Query<Task> query = datastore.db.query<Task>(ancestorKey: commitKey)..filter('name =', name);
- final List<Task> tasks = await query.run().toList();
- if (tasks.length != 1) {
- log.severe('Found ${tasks.length} entries for builder $name');
- throw InternalServerError('Expected to find 1 task for $name, but found ${tasks.length}');
- }
- return tasks.single;
- }
-
- /// Lookup [Task] from its [key].
- ///
- /// This is the fastest way to lookup [Task], but requires [id] to be passed
- /// as it is generated from Datastore.
- static Future<Task> fromKey({
- required DatastoreService datastore,
- required Key<String> commitKey,
- required int id,
- }) {
- log.fine('Looking up key...');
- final Key<int> key = Key<int>(commitKey, Task, id);
- return datastore.lookupByValue<Task>(key);
- }
-
- /// Lookup [Task] from Datastore.
- ///
- /// Either name or id must be given to lookup [Task].
- ///
- /// Prefer passing [id] when possible as it is a faster lookup.
- static Future<Task> fromDatastore({
- required DatastoreService datastore,
- required Key<String> commitKey,
- String? name,
- String? id,
- }) {
- if (id == null) {
- return Task.fromCommitKey(
- datastore: datastore,
- commitKey: commitKey,
- name: name!,
- );
- }
-
- return Task.fromKey(
- datastore: datastore,
- commitKey: commitKey,
- id: int.parse(id),
- );
- }
-
- /// Creates a [Task] based on a buildbucket [bb.Build].
- static Future<Task> fromBuildbucketBuild(
- bb.Build build,
- DatastoreService datastore, {
- String? customName,
- }) async {
- log.fine('Creating task from buildbucket result: ${build.toString()}');
- // Example: Getting "flutter" from "mirrors/flutter".
- final String repository = build.input!.gitilesCommit!.project!.split('/')[1];
- log.fine('Repository: $repository');
-
- // Example: Getting "stable" from "refs/heads/stable".
- final String branch = build.input!.gitilesCommit!.ref!.split('/')[2];
- log.fine('Branch: $branch');
-
- final String hash = build.input!.gitilesCommit!.hash!;
- log.fine('Hash: $hash');
-
- final RepositorySlug slug = RepositorySlug('flutter', repository);
- log.fine('Slug: ${slug.toString()}');
-
- final int startTime = build.startTime?.millisecondsSinceEpoch ?? 0;
- final int endTime = build.endTime?.millisecondsSinceEpoch ?? 0;
- log.fine('Start/end time (ms): $startTime, $endTime');
-
- final String id = '${slug.fullName}/$branch/$hash';
- final Key<String> commitKey = datastore.db.emptyKey.append<String>(Commit, id: id);
- final Commit commit = await datastore.db.lookupValue<Commit>(commitKey);
- final task = Task(
- attempts: 1,
- buildNumber: build.number,
- buildNumberList: build.number.toString(),
- builderName: build.builderId.builder,
- commitKey: commitKey,
- createTimestamp: startTime,
- endTimestamp: endTime,
- luciBucket: build.builderId.bucket,
- name: customName ?? build.builderId.builder,
- stageName: build.builderId.project,
- startTimestamp: startTime,
- status: convertBuildbucketStatusToString(build.status!),
- key: commit.key.append(Task),
- timeoutInMinutes: 0,
- reason: '',
- requiredCapabilities: [],
- reservedForAgentId: '',
- );
- return task;
- }
-
- /// Converts a buildbucket status to a task status.
- static String convertBuildbucketStatusToString(bb.Status status) {
- switch (status) {
- case bb.Status.success:
- return statusSucceeded;
- case bb.Status.canceled:
- return statusCancelled;
- case bb.Status.infraFailure:
- return statusInfraFailure;
- case bb.Status.started:
- return statusInProgress;
- case bb.Status.scheduled:
- return statusNew;
- default:
- return statusFailed;
- }
- }
-
- /// The task was cancelled.
- static const String statusCancelled = 'Cancelled';
-
- /// The task is yet to be run.
- static const String statusNew = 'New';
-
- /// The task failed to run due to an unexpected issue.
- static const String statusInfraFailure = 'Infra Failure';
-
- /// The task is currently running.
- static const String statusInProgress = 'In Progress';
-
- /// The task was run successfully.
- static const String statusSucceeded = 'Succeeded';
-
- /// The task failed to run successfully.
- static const String statusFailed = 'Failed';
-
- /// The task was skipped or canceled while running.
- ///
- /// This status is only used by LUCI tasks.
- static const String statusSkipped = 'Skipped';
-
- /// The list of legal values for the [status] property.
- static const List<String> legalStatusValues = <String>[
- statusCancelled,
- statusFailed,
- statusInfraFailure,
- statusInProgress,
- statusNew,
- statusSkipped,
- statusSucceeded,
- ];
-
- static const List<String> finishedStatusValues = <String>[
- statusCancelled,
- statusFailed,
- statusInfraFailure,
- statusSkipped,
- statusSucceeded,
- ];
-
- /// The key of the commit that owns this task.
- @ModelKeyProperty(propertyName: 'ChecklistKey')
- @JsonKey(name: 'ChecklistKey')
- @StringKeyConverter()
- Key<String>? commitKey;
-
- /// The timestamp (in milliseconds since the Epoch) that this task was
- /// created.
- ///
- /// This is _not_ when the task first started running, as tasks start out in
- /// the 'New' state until they've been picked up by an [Agent].
- @IntProperty(propertyName: 'CreateTimestamp', required: true)
- @JsonKey(name: 'CreateTimestamp')
- int? createTimestamp;
-
- /// The timestamp (in milliseconds since the Epoch) that this task started
- /// running.
- ///
- /// Tasks may be run more than once. If this task has been run more than
- /// once, this timestamp represents when the task was most recently started.
- @IntProperty(propertyName: 'StartTimestamp', required: true)
- @JsonKey(name: 'StartTimestamp')
- int? startTimestamp;
-
- /// The timestamp (in milliseconds since the Epoch) that this task last
- /// finished running.
- @IntProperty(propertyName: 'EndTimestamp', required: true)
- @JsonKey(name: 'EndTimestamp')
- int? endTimestamp;
-
- /// The name of the task.
- ///
- /// This is a human-readable name, typically a test name (e.g.
- /// "hello_world__memory").
- @StringProperty(propertyName: 'Name', required: true)
- @JsonKey(name: 'Name')
- String? name;
-
- /// The number of attempts that have been made to run this task successfully.
- ///
- /// New tasks that have not yet been picked up by an [Agent] will have zero
- /// attempts.
- @IntProperty(propertyName: 'Attempts', required: true)
- @JsonKey(name: 'Attempts')
- int? attempts;
-
- /// Whether this task has been marked flaky by .ci.yaml.
- ///
- /// See also:
- ///
- /// * <https://github.com/flutter/flutter/blob/master/.ci.yaml>
- ///
- /// A flaky (`bringup: true`) task will not block the tree.
- @BoolProperty(propertyName: 'Flaky')
- @JsonKey(name: 'Flaky')
- bool? isFlaky;
-
- /// Whether the test execution of this task shows flake.
- ///
- /// Test runner supports rerun, and this flag tracks if a flake happens.
- ///
- /// See also:
- /// * <https://github.com/flutter/flutter/blob/master/dev/devicelab/lib/framework/runner.dart>
- @BoolProperty(propertyName: 'TestFlaky')
- @JsonKey(name: 'TestFlaky')
- bool? isTestFlaky;
-
- /// The timeout of the task, or zero if the task has no timeout.
- @IntProperty(propertyName: 'TimeoutInMinutes', required: true)
- @JsonKey(name: 'TimeoutInMinutes')
- int? timeoutInMinutes;
-
- /// Currently unset and unused.
- @StringProperty(propertyName: 'Reason')
- @JsonKey(name: 'Reason')
- String? reason;
-
- /// The build number of luci build: https://chromium.googlesource.com/infra/luci/luci-go/+/master/buildbucket/proto/build.proto#146
- @IntProperty(propertyName: 'BuildNumber')
- @JsonKey(name: 'BuildNumber')
- int? buildNumber;
-
- /// The build number list of luci builds: comma joined string of
- /// different build numbers.
- ///
- /// For the case with single run 123, [buildNumberList] = '123';
- /// For the case with multiple reruns 123, 456, 789,
- /// [buildNumberList] = '123,456,789'.
- @StringProperty(propertyName: 'BuildNumberList')
- @JsonKey(name: 'BuildNumberList')
- String? buildNumberList;
-
- /// The builder name of luci build.
- @StringProperty(propertyName: 'BuilderName')
- @JsonKey(name: 'BuilderName')
- String? builderName;
-
- /// The luci pool where the luci task runs.
- @StringProperty(propertyName: 'LuciBucket')
- @JsonKey(name: 'luciBucket')
- String? luciBucket;
-
- /// The list of capabilities that agents are required to have to run this
- /// task.
- ///
- /// See also:
- ///
- /// * [Agent.capabilities], which list the capabilities of an agent.
- @StringListProperty(propertyName: 'RequiredCapabilities')
- @JsonKey(name: 'RequiredCapabilities')
- List<String>? requiredCapabilities;
-
- /// Set to the ID of the agent that's responsible for running this task.
- ///
- /// This will be null until an agent has reserved this task.
- @StringProperty(propertyName: 'ReservedForAgentID')
- @JsonKey(name: 'ReservedForAgentID')
- String? reservedForAgentId;
-
- /// The name of the [Stage] that groups this task with other tasks that are
- /// related to it.
- @StringProperty(propertyName: 'StageName', required: true)
- @JsonKey(name: 'StageName')
- String? stageName;
-
- /// The status of the task.
- ///
- /// Legal values and their meanings are defined in [legalStatusValues].
- @StringProperty(propertyName: 'Status', required: true)
- @JsonKey(name: 'Status')
- String get status => _status ?? statusNew;
- String? _status;
- set status(String value) {
- if (!legalStatusValues.contains(value)) {
- throw ArgumentError('Invalid state: "$value"');
- }
- _status = value;
- }
-
- /// Update [Task] fields based on a LUCI [Build].
- void updateFromBuild(Build build) {
- final List<String>? tags = build.tags;
- // Example tag: build_address:luci.flutter.prod/Linux Cocoon/271
- final String? buildAddress = tags?.firstWhere((String tag) => tag.contains('build_address'));
- if (buildAddress == null) {
- log.warning('Tags: $tags');
- throw const BadRequestException('build_address does not contain build number');
- }
-
- final int currentBuildNumber = int.parse(buildAddress.split('/').last);
- if (buildNumber == null || buildNumber! < currentBuildNumber) {
- buildNumber = currentBuildNumber;
- } else if (currentBuildNumber < buildNumber!) {
- log.fine('Skipping message as build number is before the current task');
- return;
- }
-
- if (buildNumberList == null) {
- buildNumberList = '$buildNumber';
- } else {
- final Set<String> buildNumberSet = buildNumberList!.split(',').toSet();
- buildNumberSet.add(buildNumber.toString());
- buildNumberList = buildNumberSet.join(',');
- }
-
- createTimestamp = build.createdTimestamp?.millisecondsSinceEpoch ?? 0;
- startTimestamp = build.startedTimestamp?.millisecondsSinceEpoch ?? 0;
- endTimestamp = build.completedTimestamp?.millisecondsSinceEpoch ?? 0;
-
- _setStatusFromLuciStatus(build);
- }
-
- /// Updates [Task] based on a Buildbucket [Build].
- void updateFromBuildbucketBuild(bb.Build build) {
- buildNumber = build.number!;
-
- if (buildNumberList == null) {
- buildNumberList = '$buildNumber';
- } else {
- final Set<String> buildNumberSet = buildNumberList!.split(',').toSet();
- buildNumberSet.add(buildNumber.toString());
- buildNumberList = buildNumberSet.join(',');
- }
-
- createTimestamp = build.startTime?.millisecondsSinceEpoch ?? 0;
- startTimestamp = build.startTime?.millisecondsSinceEpoch ?? 0;
- endTimestamp = build.endTime?.millisecondsSinceEpoch ?? 0;
-
- attempts = buildNumberList!.split(',').length;
-
- status = convertBuildbucketStatusToString(build.status!);
- }
-
- /// Get a [Task] status from a LUCI [Build] status/result.
- String _setStatusFromLuciStatus(Build build) {
- // Updates can come out of order. Ensure completed statuses are kept.
- if (_isStatusCompleted()) {
- return status;
- }
-
- if (build.status == Status.started) {
- return status = statusInProgress;
- }
- switch (build.result) {
- case Result.success:
- return status = statusSucceeded;
- case Result.canceled:
- return status = statusCancelled;
- case Result.failure:
- // Note that `Result` does not support `infraFailure`:
- // https://github.com/luci/luci-go/blob/main/common/api/buildbucket/buildbucket/v1/buildbucket-gen.go#L247-L251
- // To determine an infra failure status, we need to combine `Result.failure` and `FailureReason.infraFailure`.
- if (build.failureReason == FailureReason.infraFailure) {
- return status = statusInfraFailure;
- } else {
- return status = statusFailed;
- }
- default:
- throw BadRequestException('${build.result} is unknown');
- }
- }
-
- bool _isStatusCompleted() {
- const List<String> completedStatuses = <String>[
- statusCancelled,
- statusFailed,
- statusInfraFailure,
- statusSucceeded,
- ];
- return completedStatuses.contains(status);
- }
-
- /// Comparator that sorts tasks by fewest attempts first.
- static int byAttempts(Task a, Task b) => a.attempts!.compareTo(b.attempts!);
-
- /// Serializes this object to a JSON primitive.
- Map<String, dynamic> toJson() => _$TaskToJson(this);
-
- @override
- String toString() {
- final StringBuffer buf = StringBuffer()
- ..write('$runtimeType(')
- ..write('id: $id')
- ..write(', parentKey: ${parentKey?.id}')
- ..write(', key: ${parentKey == null ? null : key.id}')
- ..write(', commitKey: ${commitKey?.id}')
- ..write(', createTimestamp: $createTimestamp')
- ..write(', startTimestamp: $startTimestamp')
- ..write(', endTimestamp: $endTimestamp')
- ..write(', name: $name')
- ..write(', attempts: $attempts')
- ..write(', isFlaky: $isFlaky')
- ..write(', isTestRunFlaky: $isTestFlaky')
- ..write(', timeoutInMinutes: $timeoutInMinutes')
- ..write(', reason: $reason')
- ..write(', requiredCapabilities: $requiredCapabilities')
- ..write(', reservedForAgentId: $reservedForAgentId')
- ..write(', stageName: $stageName')
- ..write(', status: $status')
- ..write(', buildNumber: $buildNumber')
- ..write(', buildNumberList: $buildNumberList')
- ..write(', builderName: $builderName')
- ..write(', luciBucket: $luciBucket')
- ..write(')');
- return buf.toString();
- }
-}
-
-Iterable<Task> targetsToTask(Commit commit, List<Target> targets) =>
- targets.map((Target target) => Task.fromTarget(commit: commit, target: target));
-
-/// The serialized representation of a [Task].
-// TODO(tvolkert): Directly serialize [Task] once frontends migrate to new serialization format.
-@JsonSerializable(createFactory: false)
-class SerializableTask {
- const SerializableTask(this.task);
-
- @JsonKey(name: 'Task')
- final Task task;
-
- @JsonKey(name: 'Key')
- @IntKeyConverter()
- Key<int> get key => task.key;
-
- /// Serializes this object to a JSON primitive.
- Map<String, dynamic> toJson() => _$SerializableTaskToJson(this);
-}
-
-/// A [Task], paired with its associated parent [Commit].
-///
-/// The [Task] model object references its parent [Commit] through the
-/// [Task.commitKey] field, but it does not hold a reference to the associated
-/// [Commit] object (just the relational mapping). This class exists for those
-/// times when the caller has loaded the associated commit from the datastore
-/// and would like to pass both the task its commit around.
-class FullTask {
- /// Creates a new [FullTask].
- const FullTask(this.task, this.commit);
-
- /// The [Task] object.
- final Task task;
-
- /// The [Commit] object references by this [task]'s [Task.commitKey].
- final Commit commit;
-}
diff --git a/app_dart/lib/src/model/appengine/task.g.dart b/app_dart/lib/src/model/appengine/task.g.dart
deleted file mode 100644
index 9ef04d6..0000000
--- a/app_dart/lib/src/model/appengine/task.g.dart
+++ /dev/null
@@ -1,35 +0,0 @@
-// GENERATED CODE - DO NOT MODIFY BY HAND
-
-// ignore_for_file: always_specify_types, implicit_dynamic_parameter
-
-part of 'task.dart';
-
-// **************************************************************************
-// JsonSerializableGenerator
-// **************************************************************************
-
-Map<String, dynamic> _$TaskToJson(Task instance) => <String, dynamic>{
- 'ChecklistKey': const StringKeyConverter().toJson(instance.commitKey),
- 'CreateTimestamp': instance.createTimestamp,
- 'StartTimestamp': instance.startTimestamp,
- 'EndTimestamp': instance.endTimestamp,
- 'Name': instance.name,
- 'Attempts': instance.attempts,
- 'Flaky': instance.isFlaky,
- 'TestFlaky': instance.isTestFlaky,
- 'TimeoutInMinutes': instance.timeoutInMinutes,
- 'Reason': instance.reason,
- 'BuildNumber': instance.buildNumber,
- 'BuildNumberList': instance.buildNumberList,
- 'BuilderName': instance.builderName,
- 'luciBucket': instance.luciBucket,
- 'RequiredCapabilities': instance.requiredCapabilities,
- 'ReservedForAgentID': instance.reservedForAgentId,
- 'StageName': instance.stageName,
- 'Status': instance.status,
- };
-
-Map<String, dynamic> _$SerializableTaskToJson(SerializableTask instance) => <String, dynamic>{
- 'Task': instance.task,
- 'Key': const IntKeyConverter().toJson(instance.key),
- };
diff --git a/app_dart/lib/src/model/ci_yaml/ci_yaml.dart b/app_dart/lib/src/model/ci_yaml/ci_yaml.dart
deleted file mode 100644
index 46f1832..0000000
--- a/app_dart/lib/src/model/ci_yaml/ci_yaml.dart
+++ /dev/null
@@ -1,308 +0,0 @@
-// Copyright 2021 The Flutter 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:convert';
-
-import 'package:cocoon_service/cocoon_service.dart';
-import 'package:github/github.dart';
-
-import '../proto/internal/scheduler.pb.dart' as pb;
-import 'target.dart';
-
-/// This is a wrapper class around S[pb.SchedulerConfig].
-///
-/// See //CI_YAML.md for high level documentation.
-class CiYaml {
- /// Creates [CiYaml] from a [RepositorySlug], [branch], [pb.SchedulerConfig] and an optional [CiYaml] of tip of tree CiYaml.
- ///
- /// If [totConfig] is passed, the validation will verify no new targets have been added that may temporarily break the LUCI infrastructure (such as new prod or presubmit targets).
- CiYaml({
- required this.slug,
- required this.branch,
- required this.config,
- CiYaml? totConfig,
- bool validate = false,
- }) {
- if (validate) {
- _validate(config, branch, totSchedulerConfig: totConfig?.config);
- }
- // Do not filter bringup targets. They are required for backward compatibility
- // with release candidate branches.
- final Iterable<Target> totTargets = totConfig?._targets ?? <Target>[];
- final List<Target> totEnabledTargets = _filterEnabledTargets(totTargets);
- totTargetNames = totEnabledTargets.map((Target target) => target.value.name).toList();
- totPostsubmitTargetNames =
- totConfig?.postsubmitTargets.map((Target target) => target.value.name).toList() ?? <String>[];
- }
-
- /// List of target names used to filter target from release candidate branches
- /// that were already removed from main.
- List<String>? totTargetNames;
-
- /// List of postsubmit target names used to filter target from release candidate branches
- /// that were already removed from main.
- List<String>? totPostsubmitTargetNames;
-
- /// The underlying protobuf that contains the raw data from .ci.yaml.
- pb.SchedulerConfig config;
-
- /// The [RepositorySlug] that [config] is from.
- final RepositorySlug slug;
-
- /// The git branch currently being scheduled against.
- final String branch;
-
- /// Gets all [Target] that run on presubmit for this config.
- List<Target> get presubmitTargets {
- final Iterable<Target> presubmitTargets =
- _targets.where((Target target) => target.value.presubmit && !target.value.bringup);
- List<Target> enabledTargets = _filterEnabledTargets(presubmitTargets);
-
- if (enabledTargets.isEmpty) {
- throw Exception('$branch is not enabled for this .ci.yaml.\nAdd it to run tests against this PR.');
- }
- // Filter targets removed from main.
- if (totTargetNames!.isNotEmpty) {
- enabledTargets = filterOutdatedTargets(slug, enabledTargets, totTargetNames);
- }
- return enabledTargets;
- }
-
- /// Gets all [Target] that run on postsubmit for this config.
- List<Target> get postsubmitTargets {
- final Iterable<Target> postsubmitTargets = _targets.where((Target target) => target.value.postsubmit);
-
- List<Target> enabledTargets = _filterEnabledTargets(postsubmitTargets);
- // Filter targets removed from main.
- if (totPostsubmitTargetNames!.isNotEmpty) {
- enabledTargets = filterOutdatedTargets(slug, enabledTargets, totPostsubmitTargetNames);
- }
- // filter if release_build true if current branch is a release candidate branch.
- enabledTargets = _filterReleaseBuildTargets(enabledTargets);
- return enabledTargets;
- }
-
- /// Filters post submit targets to remove targets we do not want backfilled.
- List<Target> get backfillTargets {
- final List<Target> filteredTargets = <Target>[];
- for (Target target in postsubmitTargets) {
- final Map<String, Object> properties = target.getProperties();
- if (!properties.containsKey('backfill') || properties['backfill'] as bool) {
- filteredTargets.add(target);
- }
- }
- return filteredTargets;
- }
-
- /// Filters targets with release_build = true on release candidate branches.
- List<Target> _filterReleaseBuildTargets(List<Target> targets) {
- final List<Target> results = <Target>[];
- final bool releaseBranch = branch.contains(RegExp('^flutter-'));
- if (!releaseBranch) {
- return targets;
- }
- for (Target target in targets) {
- final Map<String, Object> properties = target.getProperties();
- if (!properties.containsKey('release_build') || !(properties['release_build'] as bool)) {
- if (!target.value.bringup) results.add(target);
- }
- }
- return results;
- }
-
- /// Filters targets that were removed from main. [slug] is the gihub
- /// slug for branch under test, [targets] is the list of targets from
- /// the branch under test and [totTargetNames] is the list of target
- /// names enabled on the default branch.
- List<Target> filterOutdatedTargets(slug, targets, totTargetNames) {
- final String defaultBranch = Config.defaultBranch(slug);
- return targets
- .where(
- (Target target) =>
- (target.value.enabledBranches.isNotEmpty && !target.value.enabledBranches.contains(defaultBranch)) ||
- totTargetNames!.contains(target.value.name),
- )
- .toList();
- }
-
- /// Filters [targets] to those that should be started immediately.
- ///
- /// Targets with a dependency are triggered when there dependency pushes a notification that it has finished.
- /// This shouldn't be confused for targets that have the property named dependency, which is used by the
- /// flutter_deps recipe module on LUCI.
- List<Target> getInitialTargets(List<Target> targets) {
- Iterable<Target> initialTargets = targets.where((Target target) => target.value.dependencies.isEmpty).toList();
- if (branch != Config.defaultBranch(slug)) {
- // Filter out bringup targets for release branches
- initialTargets = initialTargets.where((Target target) => !target.value.bringup);
- }
-
- return initialTargets.toList();
- }
-
- Iterable<Target> get _targets => config.targets.map(
- (pb.Target target) => Target(
- schedulerConfig: config,
- value: target,
- slug: slug,
- ),
- );
-
- /// Get an unfiltered list of all [targets] that are found in the ci.yaml file.
- List<Target> get targets => _targets.toList();
-
- /// Filter [targets] to only those that are expected to run for [branch].
- ///
- /// A [Target] is expected to run if:
- /// 1. [Target.enabledBranches] exists and matches [branch].
- /// 2. Otherwise, [config.enabledBranches] matches [branch].
- List<Target> _filterEnabledTargets(Iterable<Target> targets) {
- final List<Target> filteredTargets = <Target>[];
-
- // 1. Add targets with local definition
- final Iterable<Target> overrideBranchTargets =
- targets.where((Target target) => target.value.enabledBranches.isNotEmpty);
- final Iterable<Target> enabledTargets = overrideBranchTargets
- .where((Target target) => enabledBranchesMatchesCurrentBranch(target.value.enabledBranches, branch));
- filteredTargets.addAll(enabledTargets);
-
- // 2. Add targets with global definition (this is the majority of targets)
- if (enabledBranchesMatchesCurrentBranch(config.enabledBranches, branch)) {
- final Iterable<Target> defaultBranchTargets =
- targets.where((Target target) => target.value.enabledBranches.isEmpty);
- filteredTargets.addAll(defaultBranchTargets);
- }
-
- return filteredTargets;
- }
-
- /// Whether any of the possible [RegExp] in [enabledBranches] match [branch].
- static bool enabledBranchesMatchesCurrentBranch(List<String> enabledBranches, String branch) {
- final List<String> regexes = <String>[];
- for (String enabledBranch in enabledBranches) {
- // Prefix with start of line and suffix with end of line
- regexes.add('^$enabledBranch\$');
- }
- final String rawRegexp = regexes.join('|');
- final RegExp regexp = RegExp(rawRegexp);
-
- return regexp.hasMatch(branch);
- }
-
- /// Validates [pb.SchedulerConfig] extracted from [CiYaml] files.
- ///
- /// A [pb.SchedulerConfig] file is considered good if:
- /// 1. It has at least one [pb.Target] in [pb.SchedulerConfig.targets]
- /// 2. It has at least one [branch] in [pb.SchedulerConfig.enabledBranches]
- /// 3. If a second [pb.SchedulerConfig] is passed in,
- /// we compare the current list of [pb.Target] inside the current [pb.SchedulerConfig], i.e., [schedulerConfig],
- /// with the list of [pb.Target] from tip of the tree [pb.SchedulerConfig], i.e., [totSchedulerConfig].
- /// If a [pb.Target] is indentified as a new target compared to target list from tip of the tree, The new target
- /// should have its field [pb.Target.bringup] set to true.
- /// 4. no cycle should exist in the dependency graph, as tracked by map [targetGraph]
- /// 5. [pb.Target] should not depend on self
- /// 6. [pb.Target] cannot have more than 1 dependency
- /// 7. [pb.Target] should depend on target that already exist in depedency graph, and already recorded in map [targetGraph]
- void _validate(pb.SchedulerConfig schedulerConfig, String branch, {pb.SchedulerConfig? totSchedulerConfig}) {
- if (schedulerConfig.targets.isEmpty) {
- throw const FormatException('Scheduler config must have at least 1 target');
- }
-
- if (schedulerConfig.enabledBranches.isEmpty) {
- throw const FormatException('Scheduler config must have at least 1 enabled branch');
- }
-
- final Map<String, List<pb.Target>> targetGraph = <String, List<pb.Target>>{};
- final List<String> exceptions = <String>[];
- final Set<String> totTargets = <String>{};
- if (totSchedulerConfig != null) {
- for (pb.Target target in totSchedulerConfig.targets) {
- totTargets.add(target.name);
- }
- }
- // Construct [targetGraph]. With a one scan approach, cycles in the graph
- // cannot exist as it only works forward.
- for (final pb.Target target in schedulerConfig.targets) {
- if (targetGraph.containsKey(target.name)) {
- exceptions.add('ERROR: ${target.name} already exists in graph');
- } else {
- // a new build without "bringup: true"
- // link to wiki - https://github.com/flutter/flutter/wiki/Reducing-Test-Flakiness#adding-a-new-devicelab-test
- if (totTargets.isNotEmpty && !totTargets.contains(target.name) && target.bringup != true) {
- exceptions.add(
- 'ERROR: ${target.name} is a new builder added. it needs to be marked bringup: true\nIf ci.yaml wasn\'t changed, try `git fetch upstream && git merge upstream/master`',
- );
- continue;
- }
- targetGraph[target.name] = <pb.Target>[];
- // Add edges
- if (target.dependencies.isNotEmpty) {
- if (target.dependencies.length != 1) {
- exceptions
- .add('ERROR: ${target.name} has multiple dependencies which is not supported. Use only one dependency');
- } else {
- if (target.dependencies.first == target.name) {
- exceptions.add('ERROR: ${target.name} cannot depend on itself');
- } else if (targetGraph.containsKey(target.dependencies.first)) {
- targetGraph[target.dependencies.first]!.add(target);
- } else {
- exceptions.add('ERROR: ${target.name} depends on ${target.dependencies.first} which does not exist');
- }
- }
- }
- }
-
- /// Check the dependencies for the current target if it is viable and to
- /// be added to graph. Temporarily this is only being done on non-release
- /// branches.
- if (branch == Config.defaultBranch(slug)) {
- final String? dependencyJson = target.properties['dependencies'];
- if (dependencyJson != null) {
- DependencyValidator.hasVersion(dependencyJsonString: dependencyJson);
- }
- }
- }
- _checkExceptions(exceptions);
- }
-
- void _checkExceptions(List<String> exceptions) {
- if (exceptions.isNotEmpty) {
- final String fullException = exceptions.reduce((String exception, _) => '$exception\n');
- throw FormatException(fullException);
- }
- }
-}
-
-/// Class to verify the version of the dependencies in the ci.yaml config file
-/// for each target we are going to execute.
-class DependencyValidator {
- /// dependencyJsonString is guaranteed to be non empty as it must be found
- /// before this method is called.
- ///
- /// Checks a dependency string for a pinned version.
- /// If a version is found then it must not be empty or 'latest.'
- static void hasVersion({required String dependencyJsonString}) {
- final List<String> exceptions = <String>[];
-
- /// Decoded will contain a list of maps for the dependencies found.
- final List<dynamic> decoded = json.decode(dependencyJsonString) as List<dynamic>;
-
- for (Map<String, dynamic> depMap in decoded) {
- if (!depMap.containsKey('version')) {
- exceptions.add('ERROR: dependency ${depMap['dependency']} must have a version.');
- } else {
- final String version = depMap['version'] as String;
- if (version.isEmpty || version == 'latest') {
- exceptions
- .add('ERROR: dependency ${depMap['dependency']} must have a non empty, non "latest" version supplied.');
- }
- }
- }
-
- if (exceptions.isNotEmpty) {
- final String fullException = exceptions.reduce((String exception, _) => '$exception\n');
- throw FormatException(fullException);
- }
- }
-}
diff --git a/app_dart/lib/src/model/ci_yaml/target.dart b/app_dart/lib/src/model/ci_yaml/target.dart
deleted file mode 100644
index 4fa65e1..0000000
--- a/app_dart/lib/src/model/ci_yaml/target.dart
+++ /dev/null
@@ -1,281 +0,0 @@
-// Copyright 2019 The Flutter 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:convert';
-
-import 'package:cocoon_service/src/service/scheduler/policy.dart';
-import 'package:github/github.dart';
-
-import '../../service/config.dart';
-import '../luci/buildbucket.dart';
-import '../proto/internal/scheduler.pb.dart' as pb;
-
-/// Wrapper class around [pb.Target] to support aggregate properties.
-///
-/// Changes here may also need to be upstreamed in:
-/// * https://flutter.googlesource.com/infra/+/refs/heads/main/config/lib/ci_yaml/ci_yaml.star
-class Target {
- Target({
- required this.value,
- required this.schedulerConfig,
- required this.slug,
- });
-
- /// Underlying [Target] this is based on.
- final pb.Target value;
-
- /// The [SchedulerConfig] [value] is from.
- ///
- /// This is passed for necessary lookups to platform level details.
- final pb.SchedulerConfig schedulerConfig;
-
- /// The [RepositorySlug] this [Target] is run for.
- final RepositorySlug slug;
-
- /// Target prefixes that indicate it will run on an ios device.
- static const List<String> iosPlatforms = <String>['mac_ios', 'mac_arm64_ios'];
-
- /// Dimension list defined in .ci.yaml.
- static List<String> dimensionList = <String>['os', 'device_os', 'device_type', 'mac_model', 'cores', 'cpu'];
-
- static String kIgnoreFlakiness = 'ignore_flakiness';
-
- /// Gets assembled dimensions for this [pb.Target].
- ///
- /// Swarming dimension doc: https://chromium.googlesource.com/infra/luci/luci-go/+/HEAD/lucicfg/doc/README.md#swarming.dimension
- ///
- /// Target dimensions are prioritized in:
- /// 1. [pb.Target.dimensions]
- /// 1. [pb.Target.properties]
- /// 2. [schedulerConfig.platformDimensions]
- List<RequestedDimension> getDimensions() {
- final Map<String, RequestedDimension> dimensionsMap = <String, RequestedDimension>{};
-
- final Map<String, Object> platformDimensions = _getPlatformDimensions();
- for (String key in platformDimensions.keys) {
- final String value = platformDimensions[key].toString();
- dimensionsMap[key] = RequestedDimension(key: key, value: value);
- }
-
- final Map<String, Object> properties = getProperties();
- // TODO(xilaizhang): https://github.com/flutter/flutter/issues/103557
- // remove this logic after dimensions are supported in ci.yaml files
- for (String dimension in dimensionList) {
- if (properties.containsKey(dimension)) {
- final String value = properties[dimension].toString();
- dimensionsMap[dimension] = RequestedDimension(key: dimension, value: value);
- }
- }
-
- final Map<String, Object> targetDimensions = _getTargetDimensions();
- for (String key in targetDimensions.keys) {
- final String value = targetDimensions[key].toString();
- dimensionsMap[key] = RequestedDimension(key: key, value: value);
- }
-
- return dimensionsMap.values.toList();
- }
-
- /// [SchedulerPolicy] this target follows.
- ///
- /// Targets not triggered by Cocoon will not be triggered.
- ///
- /// All targets except from [Config.guaranteedSchedulingRepos] run with [BatchPolicy] to reduce queue time.
- SchedulerPolicy get schedulerPolicy {
- if (value.scheduler != pb.SchedulerSystem.cocoon) {
- return OmitPolicy();
- }
- if (Config.guaranteedSchedulingRepos.contains(slug)) {
- return GuaranteedPolicy();
- }
- return BatchPolicy();
- }
-
- /// Get the tags from the defined properties in the ci.
- ///
- /// Return an empty list if no tags are found.
- List<String> get tags {
- final Map<String, Object> properties = getProperties();
- return (properties.containsKey('tags')) ? (properties['tags'] as List).map((e) => e as String).toList() : [];
- }
-
- String get getTestName {
- final List<String> words = value.name.split(' ');
- return words.length < 2 ? words[0] : words[1];
- }
-
- /// Gets the assembled properties for this [pb.Target].
- ///
- /// Target properties are prioritized in:
- /// 1. [schedulerConfig.platformProperties]
- /// 2. [pb.Target.properties]
- Map<String, Object> getProperties() {
- final Map<String, Object> platformProperties = _getPlatformProperties();
- final Map<String, Object> properties = _getTargetProperties();
- final Map<String, Object> mergedProperties = <String, Object>{}
- ..addAll(platformProperties)
- ..addAll(properties);
-
- final List<Dependency> targetDependencies = <Dependency>[];
- if (properties.containsKey('dependencies')) {
- final List<dynamic> rawDeps = properties['dependencies'] as List<dynamic>;
- final Iterable<Dependency> deps = rawDeps.map((dynamic rawDep) => Dependency.fromJson(rawDep as Object));
- targetDependencies.addAll(deps);
- }
- final List<Dependency> platformDependencies = <Dependency>[];
- if (platformProperties.containsKey('dependencies')) {
- final List<dynamic> rawDeps = platformProperties['dependencies'] as List<dynamic>;
- final Iterable<Dependency> deps = rawDeps.map((dynamic rawDep) => Dependency.fromJson(rawDep as Object));
- platformDependencies.addAll(deps);
- }
- // Lookup map to make merging [targetDependencies] and [platformDependencies] simpler.
- final Map<String, Dependency> mergedDependencies = <String, Dependency>{};
- for (Dependency dep in platformDependencies) {
- mergedDependencies[dep.name] = dep;
- }
- for (Dependency dep in targetDependencies) {
- mergedDependencies[dep.name] = dep;
- }
- mergedProperties['dependencies'] = mergedDependencies.values.map((Dependency dep) => dep.toJson()).toList();
- mergedProperties['bringup'] = value.bringup;
-
- return mergedProperties;
- }
-
- Map<String, Object> _getTargetDimensions() {
- final Map<String, Object> dimensions = <String, Object>{};
- for (String key in value.dimensions.keys) {
- dimensions[key] = _parseProperty(key, value.dimensions[key]!);
- }
-
- return dimensions;
- }
-
- Map<String, Object> _getTargetProperties() {
- final Map<String, Object> properties = <String, Object>{};
- for (String key in value.properties.keys) {
- properties[key] = _parseProperty(key, value.properties[key]!);
- }
-
- return properties;
- }
-
- Map<String, Object> _getPlatformProperties() {
- if (!schedulerConfig.platformProperties.containsKey(getPlatform())) {
- return <String, Object>{};
- }
-
- final Map<String, String> platformProperties = schedulerConfig.platformProperties[getPlatform()]!.properties;
- final Map<String, Object> properties = <String, Object>{};
- for (String key in platformProperties.keys) {
- properties[key] = _parseProperty(key, platformProperties[key]!);
- }
-
- return properties;
- }
-
- Map<String, Object> _getPlatformDimensions() {
- if (!schedulerConfig.platformProperties.containsKey(getPlatform())) {
- return <String, Object>{};
- }
-
- final Map<String, String> platformDimensions = schedulerConfig.platformProperties[getPlatform()]!.dimensions;
- final Map<String, Object> dimensions = <String, Object>{};
- for (String key in platformDimensions.keys) {
- dimensions[key] = _parseProperty(key, platformDimensions[key]!);
- }
-
- return dimensions;
- }
-
- /// Converts property strings to their correct type.
- ///
- /// Changes made here should also be made to [_platform_properties] and [_properties] in:
- /// * https://cs.opensource.google/flutter/infra/+/main:config/lib/ci_yaml/ci_yaml.star
- Object _parseProperty(String key, String value) {
- // Yaml will escape new lines unnecessarily for strings.
- final List<String> newLineIssues = <String>['android_sdk_license', 'android_sdk_preview_license'];
- if (value == 'true') {
- return true;
- } else if (value == 'false') {
- return false;
- } else if (value.startsWith('[') || value.startsWith('{')) {
- return jsonDecode(value) as Object;
- } else if (newLineIssues.contains(key)) {
- return value.replaceAll('\\n', '\n');
- } else if (int.tryParse(value) != null) {
- return int.parse(value);
- }
-
- return value;
- }
-
- /// Get the platform of this [Target].
- ///
- /// Platform is extracted as the first word in a target's name.
- String getPlatform() {
- return value.name.split(' ').first.toLowerCase();
- }
-
- /// Indicates whether this target should be scheduled via batches.
- ///
- /// DeviceLab targets are special as they run on a host + physical device, and there is limited
- /// capacity in the labs to run them. Their platforms contain one of `android`, `ios`, and `samsung`.
- ///
- /// Mac host only targets are scheduled via patches due to high queue time. This can be relieved
- /// when we have capacity support in Q4/2022.
- bool get shouldBatchSchedule {
- final String platform = getPlatform();
- return platform.contains('android') ||
- platform.contains('ios') ||
- platform.contains('samsung') ||
- platform == 'mac';
- }
-
- /// Get the associated LUCI bucket to run this [Target] in.
- String getBucket() {
- return value.bringup ? 'staging' : 'prod';
- }
-
- /// Returns value of ignore_flakiness property.
- ///
- /// Returns the value of ignore_flakiness property if
- /// it has been specified, else default returns false.
- bool getIgnoreFlakiness() {
- final Map<String, Object> properties = getProperties();
- if (properties.containsKey(kIgnoreFlakiness)) {
- return properties[kIgnoreFlakiness] as bool;
- }
- return false;
- }
-}
-
-/// Representation of a Flutter dependency.
-///
-/// See more:
-/// * https://flutter.googlesource.com/recipes/+/refs/heads/main/recipe_modules/flutter_deps/api.py
-class Dependency {
- Dependency(this.name, this.version);
-
- /// Constructor for converting from the flutter_deps format.
- factory Dependency.fromJson(Object json) {
- final Map<String, dynamic> map = json as Map<String, dynamic>;
- return Dependency(map['dependency']! as String, map['version'] as String?);
- }
-
- /// Human readable name of the dependency.
- final String name;
-
- /// CIPD tag to use.
- ///
- /// If null, will use the version set in the flutter_deps recipe_module.
- final String? version;
-
- Map<String, Object> toJson() {
- return <String, Object>{
- 'dependency': name,
- if (version != null) 'version': version!,
- };
- }
-}
diff --git a/app_dart/lib/src/model/common/json_converters.dart b/app_dart/lib/src/model/common/json_converters.dart
deleted file mode 100644
index f10d6ac..0000000
--- a/app_dart/lib/src/model/common/json_converters.dart
+++ /dev/null
@@ -1,223 +0,0 @@
-// Copyright 2019 The Flutter 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:convert' hide json;
-import 'dart:convert' as convert show json;
-
-import 'package:json_annotation/json_annotation.dart';
-
-/// A converter for tags.
-///
-/// The JSON format is:
-///
-/// ```json
-/// [
-/// {
-/// "key": "tag_key",
-/// "value": "tag_value"
-/// }
-/// ]
-/// ```
-///
-/// Which is flattened out as a `Map<String, List<String>>`.
-class TagsConverter implements JsonConverter<Map<String?, List<String?>>?, List<dynamic>?> {
- const TagsConverter();
-
- @override
- Map<String?, List<String?>>? fromJson(List<dynamic>? json) {
- if (json == null) {
- return null;
- }
- final Map<String?, List<String?>> result = <String?, List<String?>>{};
- for (Map<String, dynamic> tag in json.cast<Map<String, dynamic>>()) {
- final String? key = tag['key'] as String?;
- result[key] ??= <String?>[];
- result[key]!.add(tag['value'] as String?);
- }
- return result;
- }
-
- @override
- List<Map<String, dynamic>>? toJson(Map<String?, List<String?>>? object) {
- if (object == null) {
- return null;
- }
- if (object.isEmpty) {
- return const <Map<String, List<String>>>[];
- }
- final List<Map<String, String>> result = <Map<String, String>>[];
- for (String? key in object.keys) {
- if (key == null) {
- continue;
- }
- final List<String?>? values = object[key];
- if (values == null) {
- continue;
- }
- for (String? value in values) {
- if (value == null) {
- continue;
- }
- result.add(<String, String>{
- 'key': key,
- 'value': value,
- });
- }
- }
- return result;
- }
-}
-
-/// A converter for a "binary" JSON field.
-///
-/// Encodes and decodes a String to and from base64.
-class Base64Converter implements JsonConverter<String, String> {
- const Base64Converter();
-
- @override
- String fromJson(String json) {
- return utf8.decode(base64.decode(json));
- }
-
- @override
- String toJson(String object) {
- return base64.encode(utf8.encode(object));
- }
-}
-
-/// A converter for "timestamp" fields encoded as microseconds since epoch.
-class MicrosecondsSinceEpochConverter implements JsonConverter<DateTime?, String?> {
- const MicrosecondsSinceEpochConverter();
-
- @override
- DateTime? fromJson(String? json) {
- if (json == null) {
- return null;
- }
- return DateTime.fromMicrosecondsSinceEpoch(int.parse(json));
- }
-
- @override
- String? toJson(DateTime? object) {
- if (object == null) {
- return null;
- }
- return object.microsecondsSinceEpoch.toString();
- }
-}
-
-/// A converter for "timestamp" fields encoded as seconds since epoch.
-class SecondsSinceEpochConverter implements JsonConverter<DateTime?, String?> {
- const SecondsSinceEpochConverter();
-
- @override
- DateTime? fromJson(String? json) {
- if (json == null) {
- return null;
- }
- return DateTime.fromMillisecondsSinceEpoch(int.parse(json) * 1000);
- }
-
- @override
- String? toJson(DateTime? dateTime) {
- if (dateTime == null) {
- return null;
- }
- final int secondsSinceEpoch = dateTime.millisecondsSinceEpoch ~/ 1000;
- return secondsSinceEpoch.toString();
- }
-}
-
-/// A converter for boolean fields encoded as strings.
-class BoolConverter implements JsonConverter<bool?, String?> {
- const BoolConverter();
-
- @override
- bool? fromJson(String? json) {
- if (json == null) {
- return null;
- }
- return json.toLowerCase() == 'true';
- }
-
- @override
- String? toJson(bool? value) {
- if (value == null) {
- return null;
- }
- return '$value';
- }
-}
-
-/// A converter for fields with nested JSON objects in String format.
-class NestedJsonConverter implements JsonConverter<Map<String, dynamic>?, String?> {
- const NestedJsonConverter();
-
- @override
- Map<String, dynamic>? fromJson(String? json) {
- if (json == null) {
- return null;
- }
- return convert.json.decode(json) as Map<String, dynamic>?;
- }
-
- @override
- String? toJson(Map<String, dynamic>? object) {
- if (object == null) {
- return null;
- }
- return convert.json.encode(object);
- }
-}
-
-const Map<String, int> _months = <String, int>{
- 'Jan': 1,
- 'Feb': 2,
- 'Mar': 3,
- 'Apr': 4,
- 'May': 5,
- 'Jun': 6,
- 'Jul': 7,
- 'Aug': 8,
- 'Sep': 9,
- 'Oct': 10,
- 'Nov': 11,
- 'Dec': 12,
-};
-
-/// Convert a DateTime format from Gerrit to [DateTime].
-///
-/// Example format is "Wed Jun 07 22:54:06 2023 +0000"
-class GerritDateTimeConverter implements JsonConverter<DateTime?, String?> {
- const GerritDateTimeConverter();
-
- @override
- DateTime? fromJson(String? json) {
- if (json == null) {
- return null;
- }
-
- final DateTime? date = DateTime.tryParse(json);
- if (date != null) {
- return date;
- }
-
- json = json.substring(4); // Trim day of the week
- final List<String> parts = json.split(' ');
- final int month = _months[parts[0]]!;
- final int year = int.parse(parts[3]);
- final int day = int.parse(parts[1]);
- final List<String> time = parts[2].split(':');
- final int hours = int.parse(time[0]);
- final int minutes = int.parse(time[1]);
- final int seconds = int.parse(time[2]);
-
- return DateTime(year, month, day, hours, minutes, seconds);
- }
-
- @override
- String? toJson(DateTime? object) {
- return object?.toIso8601String();
- }
-}
diff --git a/app_dart/lib/src/model/gerrit/commit.dart b/app_dart/lib/src/model/gerrit/commit.dart
deleted file mode 100644
index 9b3a16c..0000000
--- a/app_dart/lib/src/model/gerrit/commit.dart
+++ /dev/null
@@ -1,68 +0,0 @@
-// Copyright 2019 The Flutter 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:convert';
-
-import 'package:cocoon_service/src/model/common/json_converters.dart';
-import 'package:json_annotation/json_annotation.dart';
-
-import '../../request_handling/body.dart';
-
-part 'commit.g.dart';
-
-/// Representation of a commit on Gerrit.
-///
-/// See more:
-/// * https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#commit-info
-@JsonSerializable()
-class GerritCommit extends JsonBody {
- const GerritCommit({
- this.commit,
- this.tree,
- this.author,
- this.committer,
- this.message,
- });
-
- static GerritCommit fromJson(Map<String, dynamic> json) => _$GerritCommitFromJson(json);
-
- final String? commit;
- final String? tree;
- final GerritUser? author;
- final GerritUser? committer;
- final String? message;
-
- @override
- Map<String, dynamic> toJson() => _$GerritCommitToJson(this);
-
- @override
- String toString() => jsonEncode(toJson());
-}
-
-/// Gerrit info containing the author/comitter of a commit.
-///
-/// See more:
-/// * https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#git-person-info
-@JsonSerializable()
-class GerritUser extends JsonBody {
- const GerritUser({
- this.name,
- this.email,
- this.time,
- });
-
- static GerritUser fromJson(Map<String, dynamic> json) => _$GerritUserFromJson(json);
-
- final String? name;
- final String? email;
-
- @GerritDateTimeConverter()
- final DateTime? time;
-
- @override
- Map<String, dynamic> toJson() => _$GerritUserToJson(this);
-
- @override
- String toString() => jsonEncode(toJson());
-}
diff --git a/app_dart/lib/src/model/gerrit/commit.g.dart b/app_dart/lib/src/model/gerrit/commit.g.dart
deleted file mode 100644
index a12615d..0000000
--- a/app_dart/lib/src/model/gerrit/commit.g.dart
+++ /dev/null
@@ -1,37 +0,0 @@
-// GENERATED CODE - DO NOT MODIFY BY HAND
-
-// ignore_for_file: always_specify_types, implicit_dynamic_parameter
-
-part of 'commit.dart';
-
-// **************************************************************************
-// JsonSerializableGenerator
-// **************************************************************************
-
-GerritCommit _$GerritCommitFromJson(Map<String, dynamic> json) => GerritCommit(
- commit: json['commit'] as String?,
- tree: json['tree'] as String?,
- author: json['author'] == null ? null : GerritUser.fromJson(json['author'] as Map<String, dynamic>),
- committer: json['committer'] == null ? null : GerritUser.fromJson(json['committer'] as Map<String, dynamic>),
- message: json['message'] as String?,
- );
-
-Map<String, dynamic> _$GerritCommitToJson(GerritCommit instance) => <String, dynamic>{
- 'commit': instance.commit,
- 'tree': instance.tree,
- 'author': instance.author,
- 'committer': instance.committer,
- 'message': instance.message,
- };
-
-GerritUser _$GerritUserFromJson(Map<String, dynamic> json) => GerritUser(
- name: json['name'] as String?,
- email: json['email'] as String?,
- time: const GerritDateTimeConverter().fromJson(json['time'] as String?),
- );
-
-Map<String, dynamic> _$GerritUserToJson(GerritUser instance) => <String, dynamic>{
- 'name': instance.name,
- 'email': instance.email,
- 'time': const GerritDateTimeConverter().toJson(instance.time),
- };
diff --git a/app_dart/lib/src/model/github/checks.dart b/app_dart/lib/src/model/github/checks.dart
deleted file mode 100644
index 8bd4f3e..0000000
--- a/app_dart/lib/src/model/github/checks.dart
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2020 The Flutter 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 'package:github/github.dart' show CheckSuite, PullRequest, User, Repository;
-import 'package:github/hooks.dart' show HookEvent;
-import 'package:json_annotation/json_annotation.dart';
-
-part 'checks.g.dart';
-
-/// Data models for json messages coming from GitHub Checks API.
-///
-/// See more:
-/// * https://developer.com/v3/checks/.
-@JsonSerializable(fieldRename: FieldRename.snake)
-class CheckRunEvent extends HookEvent {
- CheckRunEvent({
- this.action,
- this.checkRun,
- this.sender,
- this.repository,
- });
-
- factory CheckRunEvent.fromJson(Map<String, dynamic> input) => _$CheckRunEventFromJson(input);
- CheckRun? checkRun;
- String? action;
- User? sender;
- Repository? repository;
-
- Map<String, dynamic> toJson() => _$CheckRunEventToJson(this);
-}
-
-@JsonSerializable(fieldRename: FieldRename.snake)
-class CheckRun {
- const CheckRun({
- this.conclusion,
- this.headSha,
- this.id,
- this.pullRequests,
- this.name,
- this.checkSuite,
- });
-
- factory CheckRun.fromJson(Map<String, dynamic> input) => _$CheckRunFromJson(input);
- final int? id;
- final String? headSha;
- final String? conclusion;
- final String? name;
- final CheckSuite? checkSuite;
- @JsonKey(name: 'pull_requests', defaultValue: <PullRequest>[])
- final List<PullRequest>? pullRequests;
-
- Map<String, dynamic> toJson() => _$CheckRunToJson(this);
-}
diff --git a/app_dart/lib/src/model/github/checks.g.dart b/app_dart/lib/src/model/github/checks.g.dart
deleted file mode 100644
index 1c80026..0000000
--- a/app_dart/lib/src/model/github/checks.g.dart
+++ /dev/null
@@ -1,44 +0,0 @@
-// GENERATED CODE - DO NOT MODIFY BY HAND
-
-// ignore_for_file: always_specify_types, implicit_dynamic_parameter
-
-part of 'checks.dart';
-
-// **************************************************************************
-// JsonSerializableGenerator
-// **************************************************************************
-
-CheckRunEvent _$CheckRunEventFromJson(Map<String, dynamic> json) => CheckRunEvent(
- action: json['action'] as String?,
- checkRun: json['check_run'] == null ? null : CheckRun.fromJson(json['check_run'] as Map<String, dynamic>),
- sender: json['sender'] == null ? null : User.fromJson(json['sender'] as Map<String, dynamic>),
- repository: json['repository'] == null ? null : Repository.fromJson(json['repository'] as Map<String, dynamic>),
- );
-
-Map<String, dynamic> _$CheckRunEventToJson(CheckRunEvent instance) => <String, dynamic>{
- 'check_run': instance.checkRun,
- 'action': instance.action,
- 'sender': instance.sender,
- 'repository': instance.repository,
- };
-
-CheckRun _$CheckRunFromJson(Map<String, dynamic> json) => CheckRun(
- conclusion: json['conclusion'] as String?,
- headSha: json['head_sha'] as String?,
- id: json['id'] as int?,
- pullRequests: (json['pull_requests'] as List<dynamic>?)
- ?.map((e) => PullRequest.fromJson(e as Map<String, dynamic>))
- .toList() ??
- [],
- name: json['name'] as String?,
- checkSuite: json['check_suite'] == null ? null : CheckSuite.fromJson(json['check_suite'] as Map<String, dynamic>),
- );
-
-Map<String, dynamic> _$CheckRunToJson(CheckRun instance) => <String, dynamic>{
- 'id': instance.id,
- 'head_sha': instance.headSha,
- 'conclusion': instance.conclusion,
- 'name': instance.name,
- 'check_suite': instance.checkSuite,
- 'pull_requests': instance.pullRequests,
- };
diff --git a/app_dart/lib/src/model/google/grpc.dart b/app_dart/lib/src/model/google/grpc.dart
deleted file mode 100644
index c3593eb..0000000
--- a/app_dart/lib/src/model/google/grpc.dart
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:json_annotation/json_annotation.dart';
-
-import '../../request_handling/body.dart';
-
-part 'grpc.g.dart';
-
-/// [Status] defines a logical error model that is suitable for
-/// different programming environments, including REST APIs and RPC APIs. It is
-/// used by [gRPC](https://github.com/grpc). Each [Status] message contains
-/// three pieces of data: error code, error message, and error details.
-///
-/// Resources:
-/// * https://cloud.google.com/apis/design/errors
-@JsonSerializable(includeIfNull: false)
-class GrpcStatus extends JsonBody {
- const GrpcStatus({required this.code, this.message, this.details});
-
- /// Creates a [Status] from JSON.
- static GrpcStatus fromJson(Map<String, dynamic> json) => _$GrpcStatusFromJson(json);
-
- /// The status code, which should be an enum value of [google.rpc.Code][].
- final int code;
-
- /// A developer-facing error message, which should be in English. Any
- /// user-facing error message should be localized and sent in the
- /// [google.rpc.Status.details][] field, or localized by the client.
- final String? message;
-
- /// A list of messages that carry the error details. There is a common set of
- /// message types for APIs to use.
- final dynamic details;
-
- @override
- String toString() => 'Response #$code: $message, $details';
-
- @override
- Map<String, dynamic> toJson() => _$GrpcStatusToJson(this);
-}
diff --git a/app_dart/lib/src/model/google/grpc.g.dart b/app_dart/lib/src/model/google/grpc.g.dart
deleted file mode 100644
index a690c98..0000000
--- a/app_dart/lib/src/model/google/grpc.g.dart
+++ /dev/null
@@ -1,31 +0,0 @@
-// GENERATED CODE - DO NOT MODIFY BY HAND
-
-// ignore_for_file: always_specify_types, implicit_dynamic_parameter
-
-part of 'grpc.dart';
-
-// **************************************************************************
-// JsonSerializableGenerator
-// **************************************************************************
-
-GrpcStatus _$GrpcStatusFromJson(Map<String, dynamic> json) => GrpcStatus(
- code: json['code'] as int,
- message: json['message'] as String?,
- details: json['details'],
- );
-
-Map<String, dynamic> _$GrpcStatusToJson(GrpcStatus instance) {
- final val = <String, dynamic>{
- 'code': instance.code,
- };
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('message', instance.message);
- writeNotNull('details', instance.details);
- return val;
-}
diff --git a/app_dart/lib/src/model/google/token_info.dart b/app_dart/lib/src/model/google/token_info.dart
deleted file mode 100644
index 6406699..0000000
--- a/app_dart/lib/src/model/google/token_info.dart
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:json_annotation/json_annotation.dart';
-
-import '../common/json_converters.dart';
-
-part 'token_info.g.dart';
-
-@JsonSerializable()
-class TokenInfo {
- /// Creates a new [TokenInfo].
- const TokenInfo({
- this.issuer,
- this.authorizedParty,
- this.audience,
- this.subject,
- this.hostedDomain,
- this.email,
- this.emailIsVerified,
- this.accessTokenHash,
- this.fullName,
- this.profilePictureUrl,
- this.givenName,
- this.familyName,
- this.locale,
- this.issued,
- this.expiration,
- this.jwtId,
- this.algorithm,
- this.keyId,
- this.encoding,
- });
-
- /// Create a new [TokenInfo] object from its JSON representation.
- factory TokenInfo.fromJson(Map<String, dynamic> json) => _$TokenInfoFromJson(json);
-
- /// The issuer of the token (e.g. "accounts.google.com").
- @JsonKey(name: 'iss')
- final String? issuer;
-
- /// The party to which the ID Token was issued.
- @JsonKey(name: 'azp')
- final String? authorizedParty;
-
- /// The token's intended audience.
- ///
- /// This should be compared against the expected OAuth client ID.
- @JsonKey(name: 'aud')
- final String? audience;
-
- /// The principal (subject) of the token.
- @JsonKey(name: 'sub')
- final String? subject;
-
- /// The user's domain.
- ///
- /// https://developers.google.com/identity/protocols/OpenIDConnect#hd-param
- @JsonKey(name: 'hd')
- final String? hostedDomain;
-
- /// The user's email address.
- @JsonKey(name: 'email')
- final String? email;
-
- /// Boolean indicating whether the user has verified their email address.
- @JsonKey(name: 'email_verified')
- @BoolConverter()
- final bool? emailIsVerified;
-
- /// Access token hash value.
- @JsonKey(name: 'at_hash')
- final String? accessTokenHash;
-
- /// The user's full name.
- @JsonKey(name: 'name')
- final String? fullName;
-
- /// URL of the user's profile picture.
- @JsonKey(name: 'picture')
- final String? profilePictureUrl;
-
- /// The user's given name.
- @JsonKey(name: 'given_name')
- final String? givenName;
-
- /// The user's family name / surname.
- @JsonKey(name: 'family_name')
- final String? familyName;
-
- /// The user's local code (e.g. "en")
- @JsonKey(name: 'locale')
- final String? locale;
-
- /// Token issuance date.
- @JsonKey(name: 'iat')
- @SecondsSinceEpochConverter()
- final DateTime? issued;
-
- /// Token expiration.
- @JsonKey(name: 'exp')
- @SecondsSinceEpochConverter()
- final DateTime? expiration;
-
- /// Unique identifier for the token itself.
- @JsonKey(name: 'jti')
- final String? jwtId;
-
- /// Encryption algorithm used to encrypt the token (e.g. "RS256").
- @JsonKey(name: 'alg')
- final String? algorithm;
-
- /// Key identifier.
- @JsonKey(name: 'kid')
- final String? keyId;
-
- /// The encoding that was used to encode the unverified token (e.g. "JWT")
- @JsonKey(name: 'typ')
- final String? encoding;
-
- /// Serializes this object to a JSON primitive.
- Map<String, dynamic> toJson() => _$TokenInfoToJson(this);
-}
diff --git a/app_dart/lib/src/model/google/token_info.g.dart b/app_dart/lib/src/model/google/token_info.g.dart
deleted file mode 100644
index 9d8de29..0000000
--- a/app_dart/lib/src/model/google/token_info.g.dart
+++ /dev/null
@@ -1,53 +0,0 @@
-// GENERATED CODE - DO NOT MODIFY BY HAND
-
-// ignore_for_file: always_specify_types, implicit_dynamic_parameter
-
-part of 'token_info.dart';
-
-// **************************************************************************
-// JsonSerializableGenerator
-// **************************************************************************
-
-TokenInfo _$TokenInfoFromJson(Map<String, dynamic> json) => TokenInfo(
- issuer: json['iss'] as String?,
- authorizedParty: json['azp'] as String?,
- audience: json['aud'] as String?,
- subject: json['sub'] as String?,
- hostedDomain: json['hd'] as String?,
- email: json['email'] as String?,
- emailIsVerified: const BoolConverter().fromJson(json['email_verified'] as String?),
- accessTokenHash: json['at_hash'] as String?,
- fullName: json['name'] as String?,
- profilePictureUrl: json['picture'] as String?,
- givenName: json['given_name'] as String?,
- familyName: json['family_name'] as String?,
- locale: json['locale'] as String?,
- issued: const SecondsSinceEpochConverter().fromJson(json['iat'] as String?),
- expiration: const SecondsSinceEpochConverter().fromJson(json['exp'] as String?),
- jwtId: json['jti'] as String?,
- algorithm: json['alg'] as String?,
- keyId: json['kid'] as String?,
- encoding: json['typ'] as String?,
- );
-
-Map<String, dynamic> _$TokenInfoToJson(TokenInfo instance) => <String, dynamic>{
- 'iss': instance.issuer,
- 'azp': instance.authorizedParty,
- 'aud': instance.audience,
- 'sub': instance.subject,
- 'hd': instance.hostedDomain,
- 'email': instance.email,
- 'email_verified': const BoolConverter().toJson(instance.emailIsVerified),
- 'at_hash': instance.accessTokenHash,
- 'name': instance.fullName,
- 'picture': instance.profilePictureUrl,
- 'given_name': instance.givenName,
- 'family_name': instance.familyName,
- 'locale': instance.locale,
- 'iat': const SecondsSinceEpochConverter().toJson(instance.issued),
- 'exp': const SecondsSinceEpochConverter().toJson(instance.expiration),
- 'jti': instance.jwtId,
- 'alg': instance.algorithm,
- 'kid': instance.keyId,
- 'typ': instance.encoding,
- };
diff --git a/app_dart/lib/src/model/luci/buildbucket.dart b/app_dart/lib/src/model/luci/buildbucket.dart
deleted file mode 100644
index 903639e..0000000
--- a/app_dart/lib/src/model/luci/buildbucket.dart
+++ /dev/null
@@ -1,928 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:json_annotation/json_annotation.dart';
-
-import '../../request_handling/body.dart';
-import '../common/json_converters.dart';
-import '../google/grpc.dart';
-
-part 'buildbucket.g.dart';
-
-// The classes in this file are based on protos found in:
-// https://chromium.googlesource.com/infra/luci/luci-go/+/master/buildbucket/proto/build.proto
-// https://chromium.googlesource.com/infra/luci/luci-go/+/master/buildbucket/proto/common.proto
-// https://chromium.googlesource.com/infra/luci/luci-go/+/master/buildbucket/proto/rpc.proto
-//
-// The `fromJson` methods in this class are static rather than factories so that
-// they can be passed as arguments to other functions looking for a parser.
-
-/// A request for the Batch RPC.
-///
-/// This message can be used to find, get, schedule, or cancel multiple builds.
-@JsonSerializable(includeIfNull: false)
-class BatchRequest extends JsonBody {
- /// Creates a request for the Batch RPC.
- const BatchRequest({
- this.requests,
- });
-
- /// Creates a [BatchRequest] from JSON.
- static BatchRequest fromJson(Map<String, dynamic> json) => _$BatchRequestFromJson(json);
-
- /// The batch of [Request]s to make.
- final List<Request>? requests;
-
- @override
- Map<String, dynamic> toJson() => _$BatchRequestToJson(this);
-
- @override
- String toString() {
- return requests.toString();
- }
-}
-
-/// A container for one request in a batch.
-///
-/// A single request must contain only one object.
-@JsonSerializable(includeIfNull: false)
-class Request extends JsonBody {
- /// Creates a request for the Batch RPC.
- ///
- /// One and only one argument should be set.
- const Request({
- this.getBuild,
- this.searchBuilds,
- this.scheduleBuild,
- this.cancelBuild,
- }) : assert(
- (getBuild != null && searchBuilds == null && scheduleBuild == null && cancelBuild == null) ||
- (getBuild == null && searchBuilds != null && scheduleBuild == null && cancelBuild == null) ||
- (getBuild == null && searchBuilds == null && scheduleBuild != null && cancelBuild == null) ||
- (getBuild == null && searchBuilds == null && scheduleBuild == null && cancelBuild != null),
- );
-
- /// Creates a [Request] object from JSON.
- static Request fromJson(Map<String, dynamic> json) => _$RequestFromJson(json);
-
- /// A request to get build information.
- final GetBuildRequest? getBuild;
-
- /// A request to find builds.
- final SearchBuildsRequest? searchBuilds;
-
- /// A request to schedule a build.
- ///
- /// All schedule build requests are executed before other requests by LUCI.
- final ScheduleBuildRequest? scheduleBuild;
-
- /// A request to cancel a build.
- final CancelBuildRequest? cancelBuild;
-
- @override
- Map<String, dynamic> toJson() => _$RequestToJson(this);
-
- @override
- String toString() {
- return getBuild?.toString() ??
- searchBuilds?.toString() ??
- scheduleBuild?.toString() ??
- cancelBuild?.toString() ??
- 'Unknown build';
- }
-}
-
-/// A response from the Batch RPC.
-@JsonSerializable(includeIfNull: false)
-class BatchResponse extends JsonBody {
- /// Creates a response for the Batch RPC.
- const BatchResponse({
- this.responses,
- });
-
- /// Creates a [BatchResponse] from JSON.
- static BatchResponse fromJson(Map<String, dynamic>? json) => _$BatchResponseFromJson(json!);
-
- /// The collected responses from the Batch request.
- final List<Response>? responses;
-
- @override
- Map<String, dynamic> toJson() => _$BatchResponseToJson(this);
-}
-
-/// An individual response from a batch request.
-@JsonSerializable(includeIfNull: false)
-class Response extends JsonBody {
- /// Creates a response for the response from the Batch RPC.
- ///
- /// One and only one of these should be set.
- const Response({
- this.getBuild,
- this.searchBuilds,
- this.scheduleBuild,
- this.cancelBuild,
- this.error,
- }) : assert(
- getBuild != null || searchBuilds != null || scheduleBuild != null || cancelBuild != null || error != null,
- );
-
- /// Creates a [Response] from JSON.
- static Response fromJson(Map<String, dynamic> json) => _$ResponseFromJson(json);
-
- /// The [Build] response corresponding to a getBuild request.
- final Build? getBuild;
-
- /// The [SearchBuildsResponse] corresponding to a searchBuilds request.
- final SearchBuildsResponse? searchBuilds;
-
- /// The [Build] response corresponding to a scheduleBuild request.
- final Build? scheduleBuild;
-
- /// The [Build] response corresponding to a cancelBuild request.
- final Build? cancelBuild;
-
- /// Error code of the unsuccessful request.
- final GrpcStatus? error;
-
- @override
- String toString() {
- if (getBuild != null) {
- return 'getBuild: $getBuild; status: $error';
- } else if (searchBuilds != null) {
- return 'searchBuilds: $searchBuilds; status: $error';
- } else if (scheduleBuild != null) {
- return 'scheduleBuild: $scheduleBuild; status: $error';
- } else if (cancelBuild != null) {
- return 'cancelBuild: $cancelBuild; status: $error';
- }
-
- return 'No response';
- }
-
- @override
- Map<String, dynamic> toJson() => _$ResponseToJson(this);
-}
-
-/// A request for the GetBuild RPC.
-@JsonSerializable(includeIfNull: false)
-class GetBuildRequest extends JsonBody {
- /// Creates a request for the GetBuild RPC.
- const GetBuildRequest({
- this.id,
- this.builderId,
- this.buildNumber,
- this.fields,
- }) : assert(
- (id == null && builderId != null && buildNumber != null) ||
- (id != null && builderId == null && buildNumber == null),
- );
-
- /// Creates a [GetBuildRequest] from JSON.
- static GetBuildRequest fromJson(Map<String, dynamic> json) => _$GetBuildRequestFromJson(json);
-
- /// The BuildBucket build ID.
- ///
- /// If specified, [builderId] and [buildNumber] must be null.
- final String? id;
-
- /// The BuildBucket [BuilderId].
- ///
- /// If specified, [buildNumber] must be specified, and [id] must be null.
- @JsonKey(name: 'builder')
- final BuilderId? builderId;
-
- /// The BuildBucket build number.
- ///
- /// If specified, [builderId] must be specified, and [id] must be null.
- final int? buildNumber;
-
- /// The list fields to be included in the response.
- ///
- /// This is a comma separated list of Build proto fields to get included
- /// in the response.
- final String? fields;
-
- @override
- Map<String, dynamic> toJson() => _$GetBuildRequestToJson(this);
-
- @override
- String toString() {
- return 'getBuild(id: $id, buildNumber: $buildNumber, field: $fields, builderId: $builderId)';
- }
-}
-
-/// A request for the GetBuilder RPC.
-@JsonSerializable(includeIfNull: false)
-class GetBuilderRequest extends JsonBody {
- /// Creates a request for the GetBuild RPC.
- const GetBuilderRequest({
- this.builderId,
- }) : assert(builderId != null);
-
- /// Creates a [GetBuilderRequest] from JSON.
- static GetBuilderRequest fromJson(Map<String, dynamic> json) => _$GetBuilderRequestFromJson(json);
-
- /// The BuildBucket builder ID.
- final BuilderId? builderId;
-
- @override
- Map<String, dynamic> toJson() => _$GetBuilderRequestToJson(this);
-
- @override
- String toString() {
- return 'getBuild(builderId: $builderId)';
- }
-}
-
-/// Configs of a builder.
-@JsonSerializable(includeIfNull: false)
-class BuilderConfig extends JsonBody {
- /// Creates a request for the GetBuild RPC.
- const BuilderConfig({
- this.name,
- }) : assert(name != null);
-
- /// Creates a [GetBuilderRequest] from JSON.
- static BuilderConfig fromJson(Map<String, dynamic> json) => _$BuilderConfigFromJson(json);
-
- /// The BuildBucket builder ID.
- final String? name;
-
- @override
- Map<String, dynamic> toJson() => _$BuilderConfigToJson(this);
-
- @override
- String toString() {
- return 'BuilderConfig(name: $name)';
- }
-}
-
-/// A configured builder.
-///
-/// https://chromium.googlesource.com/infra/luci/luci-go/+/main/buildbucket/proto/builder_common.proto
-@JsonSerializable(includeIfNull: false)
-class BuilderItem extends JsonBody {
- /// Creates a request for the GetBuild RPC.
- const BuilderItem({
- this.id,
- this.config,
- });
-
- /// Creates a [GetBuilderRequest] from JSON.
- static BuilderItem fromJson(Map<String, dynamic>? json) => _$BuilderItemFromJson(json!);
-
- /// The BuildBucket builder ID.
- final BuilderId? id;
-
- /// The BuildBucket builder config.
- final BuilderConfig? config;
-
- @override
- Map<String, dynamic> toJson() => _$BuilderItemToJson(this);
-
- @override
- String toString() {
- return 'BuilderItem(builderID: $id, builderConfig: $config)';
- }
-}
-
-/// A requrst for the ListBuilders RPC.
-@JsonSerializable(includeIfNull: false)
-class ListBuildersRequest extends JsonBody {
- /// Creates a request object for the ListBuilders RPC.
- const ListBuildersRequest({
- required this.project,
- this.bucket,
- this.pageSize = 1000,
- this.pageToken,
- });
-
- /// Creates a [ListBuildersRequest] from JSON.
- static ListBuildersRequest fromJson(Map<String, dynamic> json) => _$ListBuildersRequestFromJson(json);
-
- /// LUCI project, e.g. "flutter".
- @JsonKey(required: true)
- final String project;
-
- /// A bucket in the project, e.g. "prod".
- ///
- /// Omit to list all builders or all builders in a project.
- @JsonKey(required: false)
- final String? bucket;
-
- /// The maximum number of builders to return.
- ///
- /// The service may return fewer than this value.
- /// If unspecified, at most 100 builders will be returned.
- /// The maximum value is 1000; values above 1000 will be coerced to 1000.
- @JsonKey(required: false)
- final int? pageSize;
-
- // A page token, received from a previous `ListBuilders` call.
- // Provide this to retrieve the subsequent page.
- //
- // When paginating, all other parameters provided to `ListBuilders` MUST
- // match the call that provided the page token.
- @JsonKey(required: false)
- final String? pageToken;
-
- @override
- Map<String, dynamic> toJson() => _$ListBuildersRequestToJson(this);
-
- @override
- String toString() {
- return 'listBuilders(project: $project, bucket: $bucket, pageSize: $pageSize, pageToken: $pageToken)';
- }
-}
-
-/// The response object from a ListBuilders RPC.
-@JsonSerializable(includeIfNull: false)
-class ListBuildersResponse extends JsonBody {
- /// Creates a new response object from the ListBuilders RPC.
- ///
- /// The [nextPageToken] can be used to coninue searching if there are more
- /// builds available than the [pageSize] of the request (which is always
- /// capped at 1000). It will be null if no further builders are available.
- const ListBuildersResponse({
- this.builders,
- this.nextPageToken,
- });
-
- /// Creates a [ListBuildersResponse] from JSON.
- static ListBuildersResponse fromJson(Map<String, dynamic>? json) => _$ListBuildersResponseFromJson(json!);
-
- /// The [Builders]s returned by the search.
- final List<BuilderItem>? builders;
-
- /// A token that can be used as the [ListBuildersRequest.pageToken].
- ///
- /// This value will only be specified if further results are available;
- /// otherwise, it will be null.
- final String? nextPageToken;
-
- @override
- Map<String, dynamic> toJson() => _$ListBuildersResponseToJson(this);
-
- @override
- String toString() => builders.toString();
-}
-
-/// A request for the CancelBuild RPC.
-@JsonSerializable(includeIfNull: false)
-class CancelBuildRequest extends JsonBody {
- /// Creates a request object for the CancelBuild RPC.
- ///
- /// Both [id] and [summaryMarkdown] are required.
- const CancelBuildRequest({
- required this.id,
- required this.summaryMarkdown,
- });
-
- /// Creates a [CancelBuildRequest] from JSON.
- static CancelBuildRequest fromJson(Map<String, dynamic> json) => _$CancelBuildRequestFromJson(json);
-
- /// The BuildBucket ID for the build to cancel.
- @JsonKey(required: true)
- final String id;
-
- /// A summary of the reason for canceling.
- @JsonKey(required: true)
- final String summaryMarkdown;
-
- @override
- Map<String, dynamic> toJson() => _$CancelBuildRequestToJson(this);
-
- @override
- String toString() {
- return 'cancelBuild(id: $id, summaryMarkdown: $summaryMarkdown)';
- }
-}
-
-/// A request object for the SearchBuilds RPC.
-@JsonSerializable(includeIfNull: false)
-class SearchBuildsRequest extends JsonBody {
- /// Creates a request object for the SearchBuilds RPC.
- ///
- /// The [predicate] is required.
- ///
- /// The [pageSize] defaults to 100 if not specified.
- ///
- /// The [pageToken] from a previous request can be used to page through
- /// results.
- const SearchBuildsRequest({
- required this.predicate,
- this.pageSize,
- this.pageToken,
- this.fields,
- });
-
- /// Creates a [SearchBuildsReqeuest] object from JSON.
- static SearchBuildsRequest fromJson(Map<String, dynamic> json) => _$SearchBuildsRequestFromJson(json);
-
- /// The predicate for searching.
- final BuildPredicate predicate;
-
- /// The number of builds to return per request. Defaults to 100.
- ///
- /// Any value over 1000 is treated as 1000.
- final int? pageSize;
-
- /// The value of the [SearchBuildsResponse.nextPageToken] from a previous ]
- /// request.
- ///
- /// This can be used to continue paging through results when there are more
- /// than [pageSize] builds available.
- final String? pageToken;
-
- /// The list fields to be included in the response.
- ///
- /// This is a comma separated list of Build proto fields to get included
- /// in the response.
- final String? fields;
-
- @override
- Map<String, dynamic> toJson() => _$SearchBuildsRequestToJson(this);
-
- @override
- String toString() {
- return 'searchBuild(predicate: $predicate, pageSize: $pageSize, pageToken: $pageToken, fields: $fields)';
- }
-}
-
-/// A predicate to apply when searching for builds in the SearchBuilds RPC.
-@JsonSerializable(includeIfNull: false)
-class BuildPredicate extends JsonBody {
- /// Creates a predicate to apply when searching for builds in the SearchBuilds
- /// RPC.
- ///
- /// All items specified must match for the predicate to return.
- const BuildPredicate({
- this.builderId,
- this.status,
- this.createdBy,
- this.tags,
- this.includeExperimental,
- });
-
- /// Creates a [BuildPredicate] from JSON.
- static BuildPredicate fromJson(Map<String, dynamic> json) => _$BuildPredicateFromJson(json);
-
- /// The [BuilderId] to search for.
- @JsonKey(name: 'builder')
- final BuilderId? builderId;
-
- /// The [Status] to search for.
- final Status? status;
-
- /// Used to find builds created by the specified user.
- final String? createdBy;
-
- /// Used to return builds containing all of the specified tags.
- @TagsConverter()
- final Map<String?, List<String?>>? tags;
-
- /// Determines whether to include experimental builds in the result.
- ///
- /// Defaults to false.
- final bool? includeExperimental;
-
- @override
- Map<String, dynamic> toJson() => _$BuildPredicateToJson(this);
-
- @override
- String toString() {
- return 'buildPredicate(builderId: $builderId, status: $status, createdBy: $createdBy, tags: $tags, includeExperimental: $includeExperimental)';
- }
-}
-
-/// The response object from a SearchBuilds RPC.
-@JsonSerializable(includeIfNull: false)
-class SearchBuildsResponse extends JsonBody {
- /// Creates a new response object from the SearchBuilds RPC.
- ///
- /// The [nextPageToken] can be used to coninue searching if there are more
- /// builds available than the [pageSize] of the request (which is always
- /// capped at 1000). It will be null if no further builds are available.
- const SearchBuildsResponse({
- this.builds,
- this.nextPageToken,
- });
-
- /// Creates a [SearchBuildsResponse] from JSON.
- static SearchBuildsResponse fromJson(Map<String, dynamic>? json) => _$SearchBuildsResponseFromJson(json!);
-
- /// The [Build]s returned by the search.
- final List<Build>? builds;
-
- /// A token that can be used as the [SearchBuildsRequest.pageToken].
- ///
- /// This value will only be specified if further results are available;
- /// otherwise, it will be null.
- final String? nextPageToken;
-
- @override
- Map<String, dynamic> toJson() => _$SearchBuildsResponseToJson(this);
-
- @override
- String toString() => builds.toString();
-}
-
-/// A request object for the ScheduleBuild RPC.
-@JsonSerializable(includeIfNull: false)
-class ScheduleBuildRequest extends JsonBody {
- /// Creates a new request object for the ScheduleBuild RPC.
- ///
- /// The [requestId] is "strongly recommended", and is used by the back end to
- /// deduplicate recent requests.
- ///
- /// The [builderId] is required.
- const ScheduleBuildRequest({
- this.requestId,
- required this.builderId,
- this.canary,
- this.experimental,
- this.gitilesCommit,
- this.properties,
- this.dimensions,
- this.priority,
- this.tags,
- this.notify,
- this.fields,
- this.exe,
- });
-
- /// Creates a [ScheduleBuildRequest] from JSON.
- static ScheduleBuildRequest fromJson(Map<String, dynamic> json) => _$ScheduleBuildRequestFromJson(json);
-
- /// A unique identifier per request that is used by the backend to deduplicate
- /// requests.
- ///
- /// This is "strongly recommended", but not required.
- final String? requestId;
-
- /// The [BuilderId] to schedule on. Required.
- @JsonKey(name: 'builder')
- final BuilderId builderId;
-
- /// If specified, overrides the server-defined value of
- /// Build.infra.buildbucket.canary.
- final bool? canary;
-
- /// If specified, overrides the server-defined value of
- /// Build.input.experimental.
- ///
- /// This value comes into the recipe as `api.runtime.is_experimental`.
- final Trinary? experimental;
-
- /// Properties to include in Build.input.properties.
- /// Input properties of the created build are result of merging server-defined
- /// properties and properties in this field.
- /// Each property in this field defines a new or replaces an existing property
- /// on the server.
- /// If the server config does not allow overriding/adding the property, the
- /// request will fail with InvalidArgument error code.
- /// A server-defined property cannot be removed, but its value can be
- /// replaced with null.
- ///
- /// Reserved property paths:
- /// * ["buildbucket"]
- /// * ["buildername"]
- /// * ["blamelist""]
- /// * ["$recipe_engine/runtime", "is_luci"]
- /// * ["$recipe_engine/runtime", "is_experimental"]
- final Map<String, Object>? properties;
-
- /// The value for Build.input.gitiles_commit.
- ///
- /// Setting this field will cause the created build to have a "buildset"
- /// tag with value "commit/gitiles/{hostname}/{project}/+/{id}".
- ///
- /// GitilesCommit objects MUST have host, project, ref fields set.
- final GitilesCommit? gitilesCommit;
-
- /// Tags to include in Build.tags of the created build.
- ///
- /// Note: tags of the created build may include other tags defined on the
- /// server.
- @TagsConverter()
- final Map<String?, List<String?>>? tags;
-
- /// Overrides default dimensions defined by builder config or template build.
- ///
- /// A set of entries with the same key defines a new or replaces an existing
- /// dimension with the same key.
- final List<RequestedDimension>? dimensions;
-
- // If not zero, overrides swarming task priority.
- // See also Build.infra.swarming.priority.
- final int? priority;
-
- /// The topic and user data to send build status updates to.
- final NotificationConfig? notify;
-
- /// The list fields to be included in the response.
- ///
- /// This is a comma separated list of Build proto fields to get included
- /// in the response.
- final String? fields;
-
- /// The CIPD package with the recipes.
- final Map<String, dynamic>? exe;
-
- @override
- Map<String, dynamic> toJson() => _$ScheduleBuildRequestToJson(this);
-
- @override
- String toString() {
- return 'scheduleBuildRequest(requestId: $requestId, builderId: $builderId, gitilesCommit: $gitilesCommit, fields: $fields, notify: $notify, exe: $exe)';
- }
-}
-
-/// A single build, identified by an int64 [id], belonging to a builder.
-///
-/// See also:
-/// * [BuilderId]
-/// * [GetBuildRequest]
-@JsonSerializable(includeIfNull: false)
-class Build extends JsonBody {
- /// Creates a build object.
- ///
- /// The [id] and [builderId] parameter is required.
- const Build({
- required this.id,
- required this.builderId,
- this.number,
- this.createdBy,
- this.canceledBy,
- this.startTime,
- this.endTime,
- this.status,
- this.tags,
- this.input,
- this.summaryMarkdown,
- this.critical,
- });
-
- /// Creates a [Build] object from JSON.
- static Build fromJson(Map<String, dynamic>? json) => _$BuildFromJson(json!);
-
- /// The BuildBucket ID for the build. Required.
- final String id;
-
- /// The [BuilderId] for the build. Required.
- @JsonKey(name: 'builder')
- final BuilderId builderId;
-
- /// The LUCI build number for the build.
- ///
- /// This number corresponds to the order of builds, but build numbers may have
- /// gaps.
- final int? number;
-
- /// The verified LUCI identity that created the build.
- final String? createdBy;
-
- /// The verified LUCI identity that canceled the build.
- final String? canceledBy;
-
- /// The start time of the build.
- ///
- /// Required if and only if the [status] is [Status.started], [Status.success],
- /// or [Status.failure].
- final DateTime? startTime;
-
- /// The end time of the build.
- ///
- /// Required if and only if the [status] is terminal. Must not be before
- /// [startTime].
- final DateTime? endTime;
-
- /// The build status.
- ///
- /// Must be specified, and must not be [Status.unspecified].
- final Status? status;
-
- /// Human readable summary of the build in Markdown format.
- ///
- /// Up to 4kb.
- final String? summaryMarkdown;
-
- /// Arbitrary annotations for the build.
- ///
- /// The same key for a tag may be used multiple times.
- @TagsConverter()
- final Map<String?, List<String?>>? tags;
-
- /// If [Trinary.no], then the build status should not be used to assess the
- /// correctness of the input gitilesCommit or gerritChanges.
- final Trinary? critical;
-
- /// The build input values.
- final Input? input;
-
- @override
- Map<String, dynamic> toJson() => _$BuildToJson(this);
-
- @override
- String toString() => 'build(id: $id, builderId: $builderId, number: $number, status: $status, tags: $tags)';
-}
-
-/// A unique handle to a builder on BuildBucket.
-@JsonSerializable(includeIfNull: false)
-class BuilderId extends JsonBody {
- /// Creates a unique handle to a builder on BuildBucket.
- ///
- /// The bucket and builder control what ACLs for the infra, as specified in
- /// cr-buildbucket.cfg.
- const BuilderId({
- this.project,
- this.bucket,
- this.builder,
- });
-
- /// Creates a [BuilderId] object from JSON.
- static BuilderId fromJson(Map<String, dynamic> json) => _$BuilderIdFromJson(json);
-
- /// The project, e.g. "flutter", for the builder.
- final String? project;
-
- /// The bucket, e.g. "try" or "prod", for the builder.
- ///
- /// By convention, "prod" is for assets that will be released, "ci" is for
- /// reviewed code, and "try" is for untrusted code.
- final String? bucket;
-
- /// The builder from cr-buildbucket.cfg, e.g. "Linux" or "Linux Host Engine".
- final String? builder;
-
- @override
- Map<String, dynamic> toJson() => _$BuilderIdToJson(this);
-
- @override
- String toString() => '$project/$bucket/$builder';
-
- @override
- bool operator ==(Object other) =>
- other is BuilderId && other.bucket == bucket && other.builder == builder && other.project == project;
-
- @override
- int get hashCode => toString().hashCode;
-}
-
-/// Specifies a Cloud PubSub topic to send notification updates to from a
-/// [ScheduleBuildRequest].
-@JsonSerializable(includeIfNull: false)
-class NotificationConfig extends JsonBody {
- const NotificationConfig({this.pubsubTopic, this.userData});
-
- static NotificationConfig fromJson(Map<String, dynamic> json) => _$NotificationConfigFromJson(json);
-
- /// The Cloud PubSub topic to use, e.g.
- /// `projects/flutter-dashboard/topics/luci-builds`.
- final String? pubsubTopic;
-
- /// An optional user data field that will be delivered with the message.
- ///
- /// May be omitted.
- @Base64Converter()
- final String? userData;
-
- @override
- Map<String, dynamic> toJson() => _$NotificationConfigToJson(this);
-
- @override
- String toString() => 'NotificationConfig(pubsubTopic: $pubsubTopic, userData: $userData)';
-}
-
-/// The build inputs for a build.
-@JsonSerializable(includeIfNull: false)
-class Input extends JsonBody {
- /// Creates a set of build inputs for a build.
- const Input({
- this.properties,
- this.gitilesCommit,
- this.experimental,
- });
-
- /// Creates an [Input] object from JSON.
- static Input fromJson(Map<String, dynamic> json) => _$InputFromJson(json);
-
- /// The build properties of a build.
- final Map<String, Object>? properties;
-
- /// The [GitilesCommit] information for a build.
- final GitilesCommit? gitilesCommit;
-
- /// Whether the build is experimental or not. Passed into the recipe as
- /// `api.runtime.is_experimental`.
- final bool? experimental;
-
- @override
- Map<String, dynamic> toJson() => _$InputToJson(this);
-}
-
-/// A landed Git commit hosted on Gitiles.
-@JsonSerializable(includeIfNull: false)
-class GitilesCommit extends JsonBody {
- /// Creates a object corresponding to a landed Git commit hosted on Gitiles.
- const GitilesCommit({
- this.host,
- this.project,
- this.ref,
- this.hash,
- });
-
- /// Creates a [GitilesCommit] object from JSON.
- static GitilesCommit fromJson(Map<String, dynamic> json) => _$GitilesCommitFromJson(json);
-
- /// The Gitiles host name, e.g. "chromium.googlesource.com"
- final String? host;
-
- /// The repository name on the host, e.g. "external/github.com/flutter/flutter".
- final String? project;
-
- /// The Git hash of the commit.
- @JsonKey(name: 'id')
- final String? hash;
-
- /// The Git ref of the commit, e.g. "refs/heads/master".
- final String? ref;
-
- @override
- Map<String, dynamic> toJson() => _$GitilesCommitToJson(this);
-}
-
-/// Build status values.
-enum Status {
- /// Should not be used.
- @JsonValue('STATUS_UNSPECIFIED')
- unspecified,
-
- /// The status of a scheduled or pending build.
- @JsonValue('SCHEDULED')
- scheduled,
-
- /// The status of a started (running) build.
- @JsonValue('STARTED')
- started,
-
- /// A mask of `succes | failure | infraFailure | canceled`.
- @JsonValue('ENDED_MASK')
- ended,
-
- /// The build has successfully completed.
- @JsonValue('SUCCESS')
- success,
-
- /// The build has failed to complete some step due to a faulty test or commit.
- @JsonValue('FAILURE')
- failure,
-
- /// The build has failed due to an infrastructure related failure.
- @JsonValue('INFRA_FAILURE')
- infraFailure,
-
- /// The build was canceled.
- @JsonValue('CANCELED')
- canceled,
-}
-
-/// This type doesn't quite map to a bool, because there are actually four states
-/// when you include whether it's present or not.
-enum Trinary {
- /// A true value.
- @JsonValue('YES')
- yes,
-
- /// A false value.
- @JsonValue('NO')
- no,
-
- /// An explicit null value, which may or may not be treated differently from
- /// setting the JSON field to null.
- @JsonValue('UNSET')
- unset,
-}
-
-/// A requested dimension. Looks like StringPair, but also has an expiration.
-@JsonSerializable(includeIfNull: false)
-class RequestedDimension extends JsonBody {
- const RequestedDimension({
- required this.key,
- this.value,
- this.expiration,
- });
-
- static RequestedDimension fromJson(Map<String, dynamic> json) => _$RequestedDimensionFromJson(json);
-
- final String key;
- final String? value;
-
- /// If set, ignore this dimension after this duration. Must be a multiple of 1 minute. The format is '<seconds>s',
- /// e.g. '120s' represents 120 seconds.
- final String? expiration;
-
- @override
- Map<String, dynamic> toJson() => _$RequestedDimensionToJson(this);
-}
diff --git a/app_dart/lib/src/model/luci/buildbucket.g.dart b/app_dart/lib/src/model/luci/buildbucket.g.dart
deleted file mode 100644
index 6cb274c..0000000
--- a/app_dart/lib/src/model/luci/buildbucket.g.dart
+++ /dev/null
@@ -1,532 +0,0 @@
-// GENERATED CODE - DO NOT MODIFY BY HAND
-
-// ignore_for_file: always_specify_types, implicit_dynamic_parameter
-
-part of 'buildbucket.dart';
-
-// **************************************************************************
-// JsonSerializableGenerator
-// **************************************************************************
-
-BatchRequest _$BatchRequestFromJson(Map<String, dynamic> json) => BatchRequest(
- requests: (json['requests'] as List<dynamic>?)?.map((e) => Request.fromJson(e as Map<String, dynamic>)).toList(),
- );
-
-Map<String, dynamic> _$BatchRequestToJson(BatchRequest instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('requests', instance.requests);
- return val;
-}
-
-Request _$RequestFromJson(Map<String, dynamic> json) => Request(
- getBuild: json['getBuild'] == null ? null : GetBuildRequest.fromJson(json['getBuild'] as Map<String, dynamic>),
- searchBuilds: json['searchBuilds'] == null
- ? null
- : SearchBuildsRequest.fromJson(json['searchBuilds'] as Map<String, dynamic>),
- scheduleBuild: json['scheduleBuild'] == null
- ? null
- : ScheduleBuildRequest.fromJson(json['scheduleBuild'] as Map<String, dynamic>),
- cancelBuild:
- json['cancelBuild'] == null ? null : CancelBuildRequest.fromJson(json['cancelBuild'] as Map<String, dynamic>),
- );
-
-Map<String, dynamic> _$RequestToJson(Request instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('getBuild', instance.getBuild);
- writeNotNull('searchBuilds', instance.searchBuilds);
- writeNotNull('scheduleBuild', instance.scheduleBuild);
- writeNotNull('cancelBuild', instance.cancelBuild);
- return val;
-}
-
-BatchResponse _$BatchResponseFromJson(Map<String, dynamic> json) => BatchResponse(
- responses:
- (json['responses'] as List<dynamic>?)?.map((e) => Response.fromJson(e as Map<String, dynamic>)).toList(),
- );
-
-Map<String, dynamic> _$BatchResponseToJson(BatchResponse instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('responses', instance.responses);
- return val;
-}
-
-Response _$ResponseFromJson(Map<String, dynamic> json) => Response(
- getBuild: json['getBuild'] == null ? null : Build.fromJson(json['getBuild'] as Map<String, dynamic>),
- searchBuilds: json['searchBuilds'] == null
- ? null
- : SearchBuildsResponse.fromJson(json['searchBuilds'] as Map<String, dynamic>),
- scheduleBuild:
- json['scheduleBuild'] == null ? null : Build.fromJson(json['scheduleBuild'] as Map<String, dynamic>),
- cancelBuild: json['cancelBuild'] == null ? null : Build.fromJson(json['cancelBuild'] as Map<String, dynamic>),
- error: json['error'] == null ? null : GrpcStatus.fromJson(json['error'] as Map<String, dynamic>),
- );
-
-Map<String, dynamic> _$ResponseToJson(Response instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('getBuild', instance.getBuild);
- writeNotNull('searchBuilds', instance.searchBuilds);
- writeNotNull('scheduleBuild', instance.scheduleBuild);
- writeNotNull('cancelBuild', instance.cancelBuild);
- writeNotNull('error', instance.error);
- return val;
-}
-
-GetBuildRequest _$GetBuildRequestFromJson(Map<String, dynamic> json) => GetBuildRequest(
- id: json['id'] as String?,
- builderId: json['builder'] == null ? null : BuilderId.fromJson(json['builder'] as Map<String, dynamic>),
- buildNumber: json['buildNumber'] as int?,
- fields: json['fields'] as String?,
- );
-
-Map<String, dynamic> _$GetBuildRequestToJson(GetBuildRequest instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('id', instance.id);
- writeNotNull('builder', instance.builderId);
- writeNotNull('buildNumber', instance.buildNumber);
- writeNotNull('fields', instance.fields);
- return val;
-}
-
-GetBuilderRequest _$GetBuilderRequestFromJson(Map<String, dynamic> json) => GetBuilderRequest(
- builderId: json['builderId'] == null ? null : BuilderId.fromJson(json['builderId'] as Map<String, dynamic>),
- );
-
-Map<String, dynamic> _$GetBuilderRequestToJson(GetBuilderRequest instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('builderId', instance.builderId);
- return val;
-}
-
-BuilderConfig _$BuilderConfigFromJson(Map<String, dynamic> json) => BuilderConfig(
- name: json['name'] as String?,
- );
-
-Map<String, dynamic> _$BuilderConfigToJson(BuilderConfig instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('name', instance.name);
- return val;
-}
-
-BuilderItem _$BuilderItemFromJson(Map<String, dynamic> json) => BuilderItem(
- id: json['id'] == null ? null : BuilderId.fromJson(json['id'] as Map<String, dynamic>),
- config: json['config'] == null ? null : BuilderConfig.fromJson(json['config'] as Map<String, dynamic>),
- );
-
-Map<String, dynamic> _$BuilderItemToJson(BuilderItem instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('id', instance.id);
- writeNotNull('config', instance.config);
- return val;
-}
-
-ListBuildersRequest _$ListBuildersRequestFromJson(Map<String, dynamic> json) {
- $checkKeys(
- json,
- requiredKeys: const ['project'],
- );
- return ListBuildersRequest(
- project: json['project'] as String,
- bucket: json['bucket'] as String?,
- pageSize: json['pageSize'] as int? ?? 1000,
- pageToken: json['pageToken'] as String?,
- );
-}
-
-Map<String, dynamic> _$ListBuildersRequestToJson(ListBuildersRequest instance) {
- final val = <String, dynamic>{
- 'project': instance.project,
- };
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('bucket', instance.bucket);
- writeNotNull('pageSize', instance.pageSize);
- writeNotNull('pageToken', instance.pageToken);
- return val;
-}
-
-ListBuildersResponse _$ListBuildersResponseFromJson(Map<String, dynamic> json) => ListBuildersResponse(
- builders:
- (json['builders'] as List<dynamic>?)?.map((e) => BuilderItem.fromJson(e as Map<String, dynamic>)).toList(),
- nextPageToken: json['nextPageToken'] as String?,
- );
-
-Map<String, dynamic> _$ListBuildersResponseToJson(ListBuildersResponse instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('builders', instance.builders);
- writeNotNull('nextPageToken', instance.nextPageToken);
- return val;
-}
-
-CancelBuildRequest _$CancelBuildRequestFromJson(Map<String, dynamic> json) {
- $checkKeys(
- json,
- requiredKeys: const ['id', 'summaryMarkdown'],
- );
- return CancelBuildRequest(
- id: json['id'] as String,
- summaryMarkdown: json['summaryMarkdown'] as String,
- );
-}
-
-Map<String, dynamic> _$CancelBuildRequestToJson(CancelBuildRequest instance) => <String, dynamic>{
- 'id': instance.id,
- 'summaryMarkdown': instance.summaryMarkdown,
- };
-
-SearchBuildsRequest _$SearchBuildsRequestFromJson(Map<String, dynamic> json) => SearchBuildsRequest(
- predicate: BuildPredicate.fromJson(json['predicate'] as Map<String, dynamic>),
- pageSize: json['pageSize'] as int?,
- pageToken: json['pageToken'] as String?,
- fields: json['fields'] as String?,
- );
-
-Map<String, dynamic> _$SearchBuildsRequestToJson(SearchBuildsRequest instance) {
- final val = <String, dynamic>{
- 'predicate': instance.predicate,
- };
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('pageSize', instance.pageSize);
- writeNotNull('pageToken', instance.pageToken);
- writeNotNull('fields', instance.fields);
- return val;
-}
-
-BuildPredicate _$BuildPredicateFromJson(Map<String, dynamic> json) => BuildPredicate(
- builderId: json['builder'] == null ? null : BuilderId.fromJson(json['builder'] as Map<String, dynamic>),
- status: $enumDecodeNullable(_$StatusEnumMap, json['status']),
- createdBy: json['createdBy'] as String?,
- tags: const TagsConverter().fromJson(json['tags'] as List?),
- includeExperimental: json['includeExperimental'] as bool?,
- );
-
-Map<String, dynamic> _$BuildPredicateToJson(BuildPredicate instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('builder', instance.builderId);
- writeNotNull('status', _$StatusEnumMap[instance.status]);
- writeNotNull('createdBy', instance.createdBy);
- writeNotNull('tags', const TagsConverter().toJson(instance.tags));
- writeNotNull('includeExperimental', instance.includeExperimental);
- return val;
-}
-
-const _$StatusEnumMap = {
- Status.unspecified: 'STATUS_UNSPECIFIED',
- Status.scheduled: 'SCHEDULED',
- Status.started: 'STARTED',
- Status.ended: 'ENDED_MASK',
- Status.success: 'SUCCESS',
- Status.failure: 'FAILURE',
- Status.infraFailure: 'INFRA_FAILURE',
- Status.canceled: 'CANCELED',
-};
-
-SearchBuildsResponse _$SearchBuildsResponseFromJson(Map<String, dynamic> json) => SearchBuildsResponse(
- builds: (json['builds'] as List<dynamic>?)?.map((e) => Build.fromJson(e as Map<String, dynamic>)).toList(),
- nextPageToken: json['nextPageToken'] as String?,
- );
-
-Map<String, dynamic> _$SearchBuildsResponseToJson(SearchBuildsResponse instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('builds', instance.builds);
- writeNotNull('nextPageToken', instance.nextPageToken);
- return val;
-}
-
-ScheduleBuildRequest _$ScheduleBuildRequestFromJson(Map<String, dynamic> json) => ScheduleBuildRequest(
- requestId: json['requestId'] as String?,
- builderId: BuilderId.fromJson(json['builder'] as Map<String, dynamic>),
- canary: json['canary'] as bool?,
- experimental: $enumDecodeNullable(_$TrinaryEnumMap, json['experimental']),
- gitilesCommit:
- json['gitilesCommit'] == null ? null : GitilesCommit.fromJson(json['gitilesCommit'] as Map<String, dynamic>),
- properties: (json['properties'] as Map<String, dynamic>?)?.map(
- (k, e) => MapEntry(k, e as Object),
- ),
- dimensions: (json['dimensions'] as List<dynamic>?)
- ?.map((e) => RequestedDimension.fromJson(e as Map<String, dynamic>))
- .toList(),
- priority: json['priority'] as int?,
- tags: const TagsConverter().fromJson(json['tags'] as List?),
- notify: json['notify'] == null ? null : NotificationConfig.fromJson(json['notify'] as Map<String, dynamic>),
- fields: json['fields'] as String?,
- exe: json['exe'] as Map<String, dynamic>?,
- );
-
-Map<String, dynamic> _$ScheduleBuildRequestToJson(ScheduleBuildRequest instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('requestId', instance.requestId);
- val['builder'] = instance.builderId;
- writeNotNull('canary', instance.canary);
- writeNotNull('experimental', _$TrinaryEnumMap[instance.experimental]);
- writeNotNull('properties', instance.properties);
- writeNotNull('gitilesCommit', instance.gitilesCommit);
- writeNotNull('tags', const TagsConverter().toJson(instance.tags));
- writeNotNull('dimensions', instance.dimensions);
- writeNotNull('priority', instance.priority);
- writeNotNull('notify', instance.notify);
- writeNotNull('fields', instance.fields);
- writeNotNull('exe', instance.exe);
- return val;
-}
-
-const _$TrinaryEnumMap = {
- Trinary.yes: 'YES',
- Trinary.no: 'NO',
- Trinary.unset: 'UNSET',
-};
-
-Build _$BuildFromJson(Map<String, dynamic> json) => Build(
- id: json['id'] as String,
- builderId: BuilderId.fromJson(json['builder'] as Map<String, dynamic>),
- number: json['number'] as int?,
- createdBy: json['createdBy'] as String?,
- canceledBy: json['canceledBy'] as String?,
- startTime: json['startTime'] == null ? null : DateTime.parse(json['startTime'] as String),
- endTime: json['endTime'] == null ? null : DateTime.parse(json['endTime'] as String),
- status: $enumDecodeNullable(_$StatusEnumMap, json['status']),
- tags: const TagsConverter().fromJson(json['tags'] as List?),
- input: json['input'] == null ? null : Input.fromJson(json['input'] as Map<String, dynamic>),
- summaryMarkdown: json['summaryMarkdown'] as String?,
- critical: $enumDecodeNullable(_$TrinaryEnumMap, json['critical']),
- );
-
-Map<String, dynamic> _$BuildToJson(Build instance) {
- final val = <String, dynamic>{
- 'id': instance.id,
- 'builder': instance.builderId,
- };
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('number', instance.number);
- writeNotNull('createdBy', instance.createdBy);
- writeNotNull('canceledBy', instance.canceledBy);
- writeNotNull('startTime', instance.startTime?.toIso8601String());
- writeNotNull('endTime', instance.endTime?.toIso8601String());
- writeNotNull('status', _$StatusEnumMap[instance.status]);
- writeNotNull('summaryMarkdown', instance.summaryMarkdown);
- writeNotNull('tags', const TagsConverter().toJson(instance.tags));
- writeNotNull('critical', _$TrinaryEnumMap[instance.critical]);
- writeNotNull('input', instance.input);
- return val;
-}
-
-BuilderId _$BuilderIdFromJson(Map<String, dynamic> json) => BuilderId(
- project: json['project'] as String?,
- bucket: json['bucket'] as String?,
- builder: json['builder'] as String?,
- );
-
-Map<String, dynamic> _$BuilderIdToJson(BuilderId instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('project', instance.project);
- writeNotNull('bucket', instance.bucket);
- writeNotNull('builder', instance.builder);
- return val;
-}
-
-NotificationConfig _$NotificationConfigFromJson(Map<String, dynamic> json) => NotificationConfig(
- pubsubTopic: json['pubsubTopic'] as String?,
- userData: _$JsonConverterFromJson<String, String>(json['userData'], const Base64Converter().fromJson),
- );
-
-Map<String, dynamic> _$NotificationConfigToJson(NotificationConfig instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('pubsubTopic', instance.pubsubTopic);
- writeNotNull('userData', _$JsonConverterToJson<String, String>(instance.userData, const Base64Converter().toJson));
- return val;
-}
-
-Value? _$JsonConverterFromJson<Json, Value>(
- Object? json,
- Value? Function(Json json) fromJson,
-) =>
- json == null ? null : fromJson(json as Json);
-
-Json? _$JsonConverterToJson<Json, Value>(
- Value? value,
- Json? Function(Value value) toJson,
-) =>
- value == null ? null : toJson(value);
-
-Input _$InputFromJson(Map<String, dynamic> json) => Input(
- properties: (json['properties'] as Map<String, dynamic>?)?.map(
- (k, e) => MapEntry(k, e as Object),
- ),
- gitilesCommit:
- json['gitilesCommit'] == null ? null : GitilesCommit.fromJson(json['gitilesCommit'] as Map<String, dynamic>),
- experimental: json['experimental'] as bool?,
- );
-
-Map<String, dynamic> _$InputToJson(Input instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('properties', instance.properties);
- writeNotNull('gitilesCommit', instance.gitilesCommit);
- writeNotNull('experimental', instance.experimental);
- return val;
-}
-
-GitilesCommit _$GitilesCommitFromJson(Map<String, dynamic> json) => GitilesCommit(
- host: json['host'] as String?,
- project: json['project'] as String?,
- ref: json['ref'] as String?,
- hash: json['id'] as String?,
- );
-
-Map<String, dynamic> _$GitilesCommitToJson(GitilesCommit instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('host', instance.host);
- writeNotNull('project', instance.project);
- writeNotNull('id', instance.hash);
- writeNotNull('ref', instance.ref);
- return val;
-}
-
-RequestedDimension _$RequestedDimensionFromJson(Map<String, dynamic> json) => RequestedDimension(
- key: json['key'] as String,
- value: json['value'] as String?,
- expiration: json['expiration'] as String?,
- );
-
-Map<String, dynamic> _$RequestedDimensionToJson(RequestedDimension instance) {
- final val = <String, dynamic>{
- 'key': instance.key,
- };
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('value', instance.value);
- writeNotNull('expiration', instance.expiration);
- return val;
-}
diff --git a/app_dart/lib/src/model/luci/push_message.dart b/app_dart/lib/src/model/luci/push_message.dart
deleted file mode 100644
index d7c1502..0000000
--- a/app_dart/lib/src/model/luci/push_message.dart
+++ /dev/null
@@ -1,336 +0,0 @@
-// Copyright 2019 The Flutter 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:convert';
-import 'dart:typed_data';
-
-import 'package:json_annotation/json_annotation.dart';
-
-import '../../request_handling/body.dart';
-import '../../service/logging.dart';
-import '../common/json_converters.dart';
-
-part 'push_message.g.dart';
-
-/// A Cloud PubSub push message.
-///
-/// For example:
-/// ```json
-/// {
-/// "message": {
-/// "attributes": {
-/// "key": "value"
-/// },
-/// "data": "SGVsbG8gQ2xvdWQgUHViL1N1YiEgSGVyZSBpcyBteSBtZXNzYWdlIQ==",
-/// "messageId": "136969346945"
-/// },
-/// "subscription": "projects/myproject/subscriptions/mysubscription"
-/// }
-/// ```
-///
-/// Where `data` is base64 encoded.
-///
-/// See https://cloud.google.com/pubsub/docs/push#receiving_push_messages
-@JsonSerializable(includeIfNull: false)
-class PushMessageEnvelope extends JsonBody {
- const PushMessageEnvelope({
- this.message,
- this.subscription,
- });
-
- static PushMessageEnvelope fromJson(Map<String, dynamic> json) => _$PushMessageEnvelopeFromJson(json);
-
- /// The message contents.
- final PushMessage? message;
-
- /// The name of the subscription associated with the delivery.
- final String? subscription;
-
- @override
- Map<String, dynamic> toJson() => _$PushMessageEnvelopeToJson(this);
-}
-
-/// A PubSub push message payload.
-@JsonSerializable(includeIfNull: false)
-class PushMessage extends JsonBody {
- const PushMessage({
- this.attributes,
- this.data,
- this.messageId,
- this.publishTime,
- });
-
- static PushMessage fromJson(Map<String, dynamic> json) => _$PushMessageFromJson(json);
-
- /// PubSub attributes on the message.
- final Map<String, String>? attributes;
-
- /// The raw string data of the message.
- @Base64Converter()
- final String? data;
-
- /// A identifier for the message from PubSub.
- final String? messageId;
-
- /// The time at which the message was published, populated by the server when
- /// it receives the topics.publish call.
- ///
- /// A timestamp in RFC3339 UTC "Zulu" format, with nanosecond resolution and
- /// up to nine fractional digits. Examples: "2014-10-02T15:01:23Z" and
- /// "2014-10-02T15:01:23.045123456Z".
- final String? publishTime;
-
- @override
- Map<String, dynamic> toJson() => _$PushMessageToJson(this);
-}
-
-/// The LUCI build data from a PubSub push message payload.
-@JsonSerializable(includeIfNull: false, fieldRename: FieldRename.snake)
-class BuildPushMessage extends JsonBody {
- const BuildPushMessage({
- this.build,
- this.hostname,
- String? userData,
- }) : rawUserData = userData;
-
- /// Create [BuildPushMessage] from [PushMessage].
- factory BuildPushMessage.fromPushMessage(PushMessage message) {
- final data = message.data;
- if (data == null) {
- throw const FormatException('Cannot create BuildPushMessage from null data');
- }
-
- try {
- final String decodedData = String.fromCharCodes(base64.decode(data));
- log.info('Result message from base64: $decodedData');
- return BuildPushMessage.fromJson(json.decode(decodedData) as Map<String, dynamic>);
- } on FormatException {
- log.info('Result message: $data');
- return BuildPushMessage.fromJson(json.decode(data) as Map<String, dynamic>);
- }
- }
-
- static BuildPushMessage fromJson(Map<String, dynamic> json) => _$BuildPushMessageFromJson(json);
-
- /// The Build this message is for.
- final Build? build;
-
- /// The hostname for the build, e.g. `cr-buildbucket.appspot.com`.
- final String? hostname;
-
- /// Do not use this value for anything.
- ///
- /// This value cannot be marked private due to json_serializable not
- /// generating on private fields.
- ///
- /// This value is used to generate [userData].
- @JsonKey(name: 'user_data')
- final String? rawUserData;
-
- /// User data that was included in the LUCI build request.
- Map<String, dynamic> get userData {
- if (rawUserData == null) {
- return <String, dynamic>{};
- }
-
- try {
- return json.decode(rawUserData!) as Map<String, dynamic>;
- } on FormatException {
- final Uint8List bytes = base64.decode(rawUserData!);
- final String rawJson = String.fromCharCodes(bytes);
- if (rawJson.isEmpty) {
- return <String, dynamic>{};
- }
- return json.decode(rawJson) as Map<String, dynamic>;
- }
- }
-
- @override
- Map<String, dynamic> toJson() => _$BuildPushMessageToJson(this);
-}
-
-/// See https://github.com/luci/luci-go/blob/master/common/api/buildbucket/buildbucket/v1/buildbucket-gen.go#L332ƒ
-@JsonSerializable(includeIfNull: false)
-class Build extends JsonBody {
- const Build({
- this.bucket,
- this.canary,
- this.canaryPreference,
- this.cancelationReason,
- this.completedTimestamp,
- this.createdBy,
- this.createdTimestamp,
- this.failureReason,
- this.experimental,
- this.id,
- this.buildParameters,
- this.project,
- this.result,
- this.resultDetails,
- this.serviceAccount,
- this.startedTimestamp,
- this.status,
- this.tags,
- this.updatedTimestamp,
- this.utcNowTimestamp,
- this.url,
- });
-
- static Build fromJson(Map<String, dynamic> json) => _$BuildFromJson(json);
-
- /// The BuildBucket name.
- final String? bucket;
-
- /// Whether carnary hardware was used.
- final bool? canary;
-
- /// The canary preference for `canary`.
- @JsonKey(name: 'canary_preference')
- final CanaryPreference? canaryPreference;
-
- /// The reason for canceling the build.
- @JsonKey(name: 'cancelation_reason')
- final CancelationReason? cancelationReason;
-
- /// The completion time of the build.
- @JsonKey(name: 'completed_ts')
- @MicrosecondsSinceEpochConverter()
- final DateTime? completedTimestamp;
-
- /// The user who created the build.
- @JsonKey(name: 'created_by')
- final String? createdBy;
-
- /// The creation time of the build.
- @JsonKey(name: 'created_ts')
- @MicrosecondsSinceEpochConverter()
- final DateTime? createdTimestamp;
-
- /// Whether the build was experimental or not.
- final bool? experimental;
-
- /// The reason the build failed, if it failed.
- @JsonKey(name: 'failure_reason')
- final FailureReason? failureReason;
-
- /// The unique BuildBucket ID for the build.
- final String? id;
-
- /// Parameters passed to the build.
- @JsonKey(name: 'parameters_json')
- @NestedJsonConverter()
- final Map<String, dynamic>? buildParameters;
-
- /// The BuildBucket project for the build, e.g. `flutter`.
- final String? project;
-
- /// The result of the build.
- ///
- /// If [Result.canceled], [cancelationReason] will be populated.
- ///
- /// If [Result.failure], [failureReason] will be populated.
- final Result? result;
-
- /// A JSON object that contains additional build information based on the
- /// result.
- @JsonKey(name: 'result_details_json')
- @NestedJsonConverter()
- final Map<String, dynamic>? resultDetails;
-
- /// The service account used for the build.
- @JsonKey(name: 'service_account')
- final String? serviceAccount;
-
- /// The time of the build start.
- @JsonKey(name: 'started_ts')
- @MicrosecondsSinceEpochConverter()
- final DateTime? startedTimestamp;
-
- /// The [Status] of the build.
- ///
- /// If [Status.completed], [result] will be populated.
- final Status? status;
-
- /// The swarming tags for the build.
- final List<String>? tags;
-
- /// Returns all tags matching the prefix.
- ///
- /// For example, to get the `buildset` tag(s), call `tagsByName('buildset')`;
- /// to get the `swarming_tag:os`, call `tagsByName('swarming_tag:os')`.
- List<String> tagsByName(String prefix) {
- return tags!
- .where((String tag) => tag.startsWith('$prefix:'))
- .map<String>((String tag) => tag.substring(prefix.length + 1))
- .toList();
- }
-
- /// The time of the last update to this information.
- @JsonKey(name: 'updated_ts')
- @MicrosecondsSinceEpochConverter()
- final DateTime? updatedTimestamp;
-
- /// The URL of the build.
- final String? url;
-
- /// The time used as UTC now for reference to other times in this message.
- @JsonKey(name: 'utcnow_ts')
- @MicrosecondsSinceEpochConverter()
- final DateTime? utcNowTimestamp;
-
- @override
- Map<String, dynamic> toJson() => _$BuildToJson(this);
-}
-
-/// The method to select whether canary hardware was chosen for a build.
-enum CanaryPreference {
- @JsonValue('AUTO')
- auto,
- @JsonValue('CANARY')
- canary,
- @JsonValue('PROD')
- prod,
-}
-
-/// The reason for canceling a build.
-enum CancelationReason {
- @JsonValue('CANCELED_EXPLICITLY')
- canceledExplicitly,
- @JsonValue('TIMEOUT')
- timeout,
-}
-
-/// The reason a build failed.
-enum FailureReason {
- @JsonValue('BUILDBUCKET_FAILURE')
- buildbucketFailure,
- @JsonValue('BUILD_FAILURE')
- buildFailure,
- @JsonValue('INFRA_FAILURE')
- infraFailure,
- @JsonValue('INVALID_BUILD_DEFINITION')
- invalidBuildDefinition,
-}
-
-/// The final result of a build, if its [Status] is [Status.completed].
-enum Result {
- @JsonValue('CANCELED')
- canceled,
- @JsonValue('FAILURE')
- failure,
- @JsonValue('SUCCESS')
- success,
-}
-
-/// The current status of a build.
-///
-/// If [Status.completed], then a [Result] will be present as well.
-enum Status {
- @JsonValue('COMPLETED')
- completed,
- @JsonValue('SCHEDULED')
- scheduled,
- @JsonValue('STARTED')
- started,
-}
diff --git a/app_dart/lib/src/model/luci/push_message.g.dart b/app_dart/lib/src/model/luci/push_message.g.dart
deleted file mode 100644
index 963d7b6..0000000
--- a/app_dart/lib/src/model/luci/push_message.g.dart
+++ /dev/null
@@ -1,173 +0,0 @@
-// GENERATED CODE - DO NOT MODIFY BY HAND
-
-// ignore_for_file: always_specify_types, implicit_dynamic_parameter
-
-part of 'push_message.dart';
-
-// **************************************************************************
-// JsonSerializableGenerator
-// **************************************************************************
-
-PushMessageEnvelope _$PushMessageEnvelopeFromJson(Map<String, dynamic> json) => PushMessageEnvelope(
- message: json['message'] == null ? null : PushMessage.fromJson(json['message'] as Map<String, dynamic>),
- subscription: json['subscription'] as String?,
- );
-
-Map<String, dynamic> _$PushMessageEnvelopeToJson(PushMessageEnvelope instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('message', instance.message);
- writeNotNull('subscription', instance.subscription);
- return val;
-}
-
-PushMessage _$PushMessageFromJson(Map<String, dynamic> json) => PushMessage(
- attributes: (json['attributes'] as Map<String, dynamic>?)?.map(
- (k, e) => MapEntry(k, e as String),
- ),
- data: _$JsonConverterFromJson<String, String>(json['data'], const Base64Converter().fromJson),
- messageId: json['messageId'] as String?,
- publishTime: json['publishTime'] as String?,
- );
-
-Map<String, dynamic> _$PushMessageToJson(PushMessage instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('attributes', instance.attributes);
- writeNotNull('data', _$JsonConverterToJson<String, String>(instance.data, const Base64Converter().toJson));
- writeNotNull('messageId', instance.messageId);
- writeNotNull('publishTime', instance.publishTime);
- return val;
-}
-
-Value? _$JsonConverterFromJson<Json, Value>(
- Object? json,
- Value? Function(Json json) fromJson,
-) =>
- json == null ? null : fromJson(json as Json);
-
-Json? _$JsonConverterToJson<Json, Value>(
- Value? value,
- Json? Function(Value value) toJson,
-) =>
- value == null ? null : toJson(value);
-
-BuildPushMessage _$BuildPushMessageFromJson(Map<String, dynamic> json) => BuildPushMessage(
- build: json['build'] == null ? null : Build.fromJson(json['build'] as Map<String, dynamic>),
- hostname: json['hostname'] as String?,
- userData: json['user_data'] as String?,
- );
-
-Map<String, dynamic> _$BuildPushMessageToJson(BuildPushMessage instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('build', instance.build);
- writeNotNull('hostname', instance.hostname);
- val['user_data'] = instance.userData;
- return val;
-}
-
-Build _$BuildFromJson(Map<String, dynamic> json) => Build(
- bucket: json['bucket'] as String?,
- canary: json['canary'] as bool?,
- canaryPreference: $enumDecodeNullable(_$CanaryPreferenceEnumMap, json['canary_preference']),
- cancelationReason: $enumDecodeNullable(_$CancelationReasonEnumMap, json['cancelation_reason']),
- completedTimestamp: const MicrosecondsSinceEpochConverter().fromJson(json['completed_ts'] as String?),
- createdBy: json['created_by'] as String?,
- createdTimestamp: const MicrosecondsSinceEpochConverter().fromJson(json['created_ts'] as String?),
- failureReason: $enumDecodeNullable(_$FailureReasonEnumMap, json['failure_reason']),
- experimental: json['experimental'] as bool?,
- id: json['id'] as String?,
- buildParameters: const NestedJsonConverter().fromJson(json['parameters_json'] as String?),
- project: json['project'] as String?,
- result: $enumDecodeNullable(_$ResultEnumMap, json['result']),
- resultDetails: const NestedJsonConverter().fromJson(json['result_details_json'] as String?),
- serviceAccount: json['service_account'] as String?,
- startedTimestamp: const MicrosecondsSinceEpochConverter().fromJson(json['started_ts'] as String?),
- status: $enumDecodeNullable(_$StatusEnumMap, json['status']),
- tags: (json['tags'] as List<dynamic>?)?.map((e) => e as String).toList(),
- updatedTimestamp: const MicrosecondsSinceEpochConverter().fromJson(json['updated_ts'] as String?),
- utcNowTimestamp: const MicrosecondsSinceEpochConverter().fromJson(json['utcnow_ts'] as String?),
- url: json['url'] as String?,
- );
-
-Map<String, dynamic> _$BuildToJson(Build instance) {
- final val = <String, dynamic>{};
-
- void writeNotNull(String key, dynamic value) {
- if (value != null) {
- val[key] = value;
- }
- }
-
- writeNotNull('bucket', instance.bucket);
- writeNotNull('canary', instance.canary);
- writeNotNull('canary_preference', _$CanaryPreferenceEnumMap[instance.canaryPreference]);
- writeNotNull('cancelation_reason', _$CancelationReasonEnumMap[instance.cancelationReason]);
- writeNotNull('completed_ts', const MicrosecondsSinceEpochConverter().toJson(instance.completedTimestamp));
- writeNotNull('created_by', instance.createdBy);
- writeNotNull('created_ts', const MicrosecondsSinceEpochConverter().toJson(instance.createdTimestamp));
- writeNotNull('experimental', instance.experimental);
- writeNotNull('failure_reason', _$FailureReasonEnumMap[instance.failureReason]);
- writeNotNull('id', instance.id);
- writeNotNull('parameters_json', const NestedJsonConverter().toJson(instance.buildParameters));
- writeNotNull('project', instance.project);
- writeNotNull('result', _$ResultEnumMap[instance.result]);
- writeNotNull('result_details_json', const NestedJsonConverter().toJson(instance.resultDetails));
- writeNotNull('service_account', instance.serviceAccount);
- writeNotNull('started_ts', const MicrosecondsSinceEpochConverter().toJson(instance.startedTimestamp));
- writeNotNull('status', _$StatusEnumMap[instance.status]);
- writeNotNull('tags', instance.tags);
- writeNotNull('updated_ts', const MicrosecondsSinceEpochConverter().toJson(instance.updatedTimestamp));
- writeNotNull('url', instance.url);
- writeNotNull('utcnow_ts', const MicrosecondsSinceEpochConverter().toJson(instance.utcNowTimestamp));
- return val;
-}
-
-const _$CanaryPreferenceEnumMap = {
- CanaryPreference.auto: 'AUTO',
- CanaryPreference.canary: 'CANARY',
- CanaryPreference.prod: 'PROD',
-};
-
-const _$CancelationReasonEnumMap = {
- CancelationReason.canceledExplicitly: 'CANCELED_EXPLICITLY',
- CancelationReason.timeout: 'TIMEOUT',
-};
-
-const _$FailureReasonEnumMap = {
- FailureReason.buildbucketFailure: 'BUILDBUCKET_FAILURE',
- FailureReason.buildFailure: 'BUILD_FAILURE',
- FailureReason.infraFailure: 'INFRA_FAILURE',
- FailureReason.invalidBuildDefinition: 'INVALID_BUILD_DEFINITION',
-};
-
-const _$ResultEnumMap = {
- Result.canceled: 'CANCELED',
- Result.failure: 'FAILURE',
- Result.success: 'SUCCESS',
-};
-
-const _$StatusEnumMap = {
- Status.completed: 'COMPLETED',
- Status.scheduled: 'SCHEDULED',
- Status.started: 'STARTED',
-};
diff --git a/app_dart/lib/src/model/proto/internal/build_status_response.pb.dart b/app_dart/lib/src/model/proto/internal/build_status_response.pb.dart
deleted file mode 100644
index dc5a7eb..0000000
--- a/app_dart/lib/src/model/proto/internal/build_status_response.pb.dart
+++ /dev/null
@@ -1,80 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/proto/internal/build_status_response.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
-
-import 'dart:core' as $core;
-
-import 'package:protobuf/protobuf.dart' as $pb;
-
-import 'build_status_response.pbenum.dart';
-
-export 'build_status_response.pbenum.dart';
-
-class BuildStatusResponse extends $pb.GeneratedMessage {
- static final $pb.BuilderInfo _i = $pb.BuilderInfo(
- const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'BuildStatusResponse',
- package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'cocoon'),
- createEmptyInstance: create)
- ..e<EnumBuildStatus>(
- 1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'buildStatus', $pb.PbFieldType.OE,
- defaultOrMaker: EnumBuildStatus.success, valueOf: EnumBuildStatus.valueOf, enumValues: EnumBuildStatus.values)
- ..pPS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'failingTasks')
- ..hasRequiredFields = false;
-
- BuildStatusResponse._() : super();
- factory BuildStatusResponse({
- EnumBuildStatus? buildStatus,
- $core.Iterable<$core.String>? failingTasks,
- }) {
- final _result = create();
- if (buildStatus != null) {
- _result.buildStatus = buildStatus;
- }
- if (failingTasks != null) {
- _result.failingTasks.addAll(failingTasks);
- }
- return _result;
- }
- factory BuildStatusResponse.fromBuffer($core.List<$core.int> i,
- [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromBuffer(i, r);
- factory BuildStatusResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromJson(i, r);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
- 'Will be removed in next major version')
- BuildStatusResponse clone() => BuildStatusResponse()..mergeFromMessage(this);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
- 'Will be removed in next major version')
- BuildStatusResponse copyWith(void Function(BuildStatusResponse) updates) =>
- super.copyWith((message) => updates(message as BuildStatusResponse))
- as BuildStatusResponse; // ignore: deprecated_member_use
- $pb.BuilderInfo get info_ => _i;
- @$core.pragma('dart2js:noInline')
- static BuildStatusResponse create() => BuildStatusResponse._();
- BuildStatusResponse createEmptyInstance() => create();
- static $pb.PbList<BuildStatusResponse> createRepeated() => $pb.PbList<BuildStatusResponse>();
- @$core.pragma('dart2js:noInline')
- static BuildStatusResponse getDefault() =>
- _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<BuildStatusResponse>(create);
- static BuildStatusResponse? _defaultInstance;
-
- @$pb.TagNumber(1)
- EnumBuildStatus get buildStatus => $_getN(0);
- @$pb.TagNumber(1)
- set buildStatus(EnumBuildStatus v) {
- setField(1, v);
- }
-
- @$pb.TagNumber(1)
- $core.bool hasBuildStatus() => $_has(0);
- @$pb.TagNumber(1)
- void clearBuildStatus() => clearField(1);
-
- @$pb.TagNumber(2)
- $core.List<$core.String> get failingTasks => $_getList(1);
-}
diff --git a/app_dart/lib/src/model/proto/internal/build_status_response.pbenum.dart b/app_dart/lib/src/model/proto/internal/build_status_response.pbenum.dart
deleted file mode 100644
index 7663ad3..0000000
--- a/app_dart/lib/src/model/proto/internal/build_status_response.pbenum.dart
+++ /dev/null
@@ -1,27 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/proto/internal/build_status_response.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
-
-// ignore_for_file: UNDEFINED_SHOWN_NAME
-import 'dart:core' as $core;
-import 'package:protobuf/protobuf.dart' as $pb;
-
-class EnumBuildStatus extends $pb.ProtobufEnum {
- static const EnumBuildStatus success =
- EnumBuildStatus._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'success');
- static const EnumBuildStatus failure =
- EnumBuildStatus._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'failure');
-
- static const $core.List<EnumBuildStatus> values = <EnumBuildStatus>[
- success,
- failure,
- ];
-
- static final $core.Map<$core.int, EnumBuildStatus> _byValue = $pb.ProtobufEnum.initByValue(values);
- static EnumBuildStatus? valueOf($core.int value) => _byValue[value];
-
- const EnumBuildStatus._($core.int v, $core.String n) : super(v, n);
-}
diff --git a/app_dart/lib/src/model/proto/internal/build_status_response.pbjson.dart b/app_dart/lib/src/model/proto/internal/build_status_response.pbjson.dart
deleted file mode 100644
index 768fc3a..0000000
--- a/app_dart/lib/src/model/proto/internal/build_status_response.pbjson.dart
+++ /dev/null
@@ -1,35 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/proto/internal/build_status_response.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
-
-import 'dart:core' as $core;
-import 'dart:convert' as $convert;
-import 'dart:typed_data' as $typed_data;
-
-@$core.Deprecated('Use enumBuildStatusDescriptor instead')
-const EnumBuildStatus$json = const {
- '1': 'EnumBuildStatus',
- '2': const [
- const {'1': 'success', '2': 1},
- const {'1': 'failure', '2': 2},
- ],
-};
-
-/// Descriptor for `EnumBuildStatus`. Decode as a `google.protobuf.EnumDescriptorProto`.
-final $typed_data.Uint8List enumBuildStatusDescriptor =
- $convert.base64Decode('Cg9FbnVtQnVpbGRTdGF0dXMSCwoHc3VjY2VzcxABEgsKB2ZhaWx1cmUQAg==');
-@$core.Deprecated('Use buildStatusResponseDescriptor instead')
-const BuildStatusResponse$json = const {
- '1': 'BuildStatusResponse',
- '2': const [
- const {'1': 'build_status', '3': 1, '4': 1, '5': 14, '6': '.cocoon.EnumBuildStatus', '10': 'buildStatus'},
- const {'1': 'failing_tasks', '3': 2, '4': 3, '5': 9, '10': 'failingTasks'},
- ],
-};
-
-/// Descriptor for `BuildStatusResponse`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List buildStatusResponseDescriptor = $convert.base64Decode(
- 'ChNCdWlsZFN0YXR1c1Jlc3BvbnNlEjoKDGJ1aWxkX3N0YXR1cxgBIAEoDjIXLmNvY29vbi5FbnVtQnVpbGRTdGF0dXNSC2J1aWxkU3RhdHVzEiMKDWZhaWxpbmdfdGFza3MYAiADKAlSDGZhaWxpbmdUYXNrcw==');
diff --git a/app_dart/lib/src/model/proto/internal/build_status_response.pbserver.dart b/app_dart/lib/src/model/proto/internal/build_status_response.pbserver.dart
deleted file mode 100644
index 9115ba5..0000000
--- a/app_dart/lib/src/model/proto/internal/build_status_response.pbserver.dart
+++ /dev/null
@@ -1,8 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/proto/internal/build_status_response.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
-
-export 'build_status_response.pb.dart';
diff --git a/app_dart/lib/src/model/proto/internal/build_status_response.proto b/app_dart/lib/src/model/proto/internal/build_status_response.proto
deleted file mode 100644
index a562744..0000000
--- a/app_dart/lib/src/model/proto/internal/build_status_response.proto
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2019 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-syntax = "proto2";
-
-package cocoon;
-
-enum EnumBuildStatus {
- success = 1;
- failure = 2;
-}
-
-message BuildStatusResponse {
- optional EnumBuildStatus build_status = 1;
- repeated string failing_tasks = 2;
-}
\ No newline at end of file
diff --git a/app_dart/lib/src/model/proto/internal/github_webhook.pb.dart b/app_dart/lib/src/model/proto/internal/github_webhook.pb.dart
deleted file mode 100644
index 2f5fafe..0000000
--- a/app_dart/lib/src/model/proto/internal/github_webhook.pb.dart
+++ /dev/null
@@ -1,83 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/proto/internal/github_webhook.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
-
-import 'dart:core' as $core;
-
-import 'package:protobuf/protobuf.dart' as $pb;
-
-class GithubWebhookMessage extends $pb.GeneratedMessage {
- static final $pb.BuilderInfo _i = $pb.BuilderInfo(
- const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'GithubWebhookMessage',
- package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'cocoon'),
- createEmptyInstance: create)
- ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'event')
- ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'payload')
- ..hasRequiredFields = false;
-
- GithubWebhookMessage._() : super();
- factory GithubWebhookMessage({
- $core.String? event,
- $core.String? payload,
- }) {
- final _result = create();
- if (event != null) {
- _result.event = event;
- }
- if (payload != null) {
- _result.payload = payload;
- }
- return _result;
- }
- factory GithubWebhookMessage.fromBuffer($core.List<$core.int> i,
- [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromBuffer(i, r);
- factory GithubWebhookMessage.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromJson(i, r);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
- 'Will be removed in next major version')
- GithubWebhookMessage clone() => GithubWebhookMessage()..mergeFromMessage(this);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
- 'Will be removed in next major version')
- GithubWebhookMessage copyWith(void Function(GithubWebhookMessage) updates) =>
- super.copyWith((message) => updates(message as GithubWebhookMessage))
- as GithubWebhookMessage; // ignore: deprecated_member_use
- $pb.BuilderInfo get info_ => _i;
- @$core.pragma('dart2js:noInline')
- static GithubWebhookMessage create() => GithubWebhookMessage._();
- GithubWebhookMessage createEmptyInstance() => create();
- static $pb.PbList<GithubWebhookMessage> createRepeated() => $pb.PbList<GithubWebhookMessage>();
- @$core.pragma('dart2js:noInline')
- static GithubWebhookMessage getDefault() =>
- _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<GithubWebhookMessage>(create);
- static GithubWebhookMessage? _defaultInstance;
-
- @$pb.TagNumber(1)
- $core.String get event => $_getSZ(0);
- @$pb.TagNumber(1)
- set event($core.String v) {
- $_setString(0, v);
- }
-
- @$pb.TagNumber(1)
- $core.bool hasEvent() => $_has(0);
- @$pb.TagNumber(1)
- void clearEvent() => clearField(1);
-
- @$pb.TagNumber(2)
- $core.String get payload => $_getSZ(1);
- @$pb.TagNumber(2)
- set payload($core.String v) {
- $_setString(1, v);
- }
-
- @$pb.TagNumber(2)
- $core.bool hasPayload() => $_has(1);
- @$pb.TagNumber(2)
- void clearPayload() => clearField(2);
-}
diff --git a/app_dart/lib/src/model/proto/internal/github_webhook.pbenum.dart b/app_dart/lib/src/model/proto/internal/github_webhook.pbenum.dart
deleted file mode 100644
index ef33917..0000000
--- a/app_dart/lib/src/model/proto/internal/github_webhook.pbenum.dart
+++ /dev/null
@@ -1,6 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/proto/internal/github_webhook.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
diff --git a/app_dart/lib/src/model/proto/internal/github_webhook.pbjson.dart b/app_dart/lib/src/model/proto/internal/github_webhook.pbjson.dart
deleted file mode 100644
index 718d1b9..0000000
--- a/app_dart/lib/src/model/proto/internal/github_webhook.pbjson.dart
+++ /dev/null
@@ -1,23 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/proto/internal/github_webhook.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
-
-import 'dart:core' as $core;
-import 'dart:convert' as $convert;
-import 'dart:typed_data' as $typed_data;
-
-@$core.Deprecated('Use githubWebhookMessageDescriptor instead')
-const GithubWebhookMessage$json = const {
- '1': 'GithubWebhookMessage',
- '2': const [
- const {'1': 'event', '3': 1, '4': 1, '5': 9, '10': 'event'},
- const {'1': 'payload', '3': 2, '4': 1, '5': 9, '10': 'payload'},
- ],
-};
-
-/// Descriptor for `GithubWebhookMessage`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List githubWebhookMessageDescriptor = $convert
- .base64Decode('ChRHaXRodWJXZWJob29rTWVzc2FnZRIUCgVldmVudBgBIAEoCVIFZXZlbnQSGAoHcGF5bG9hZBgCIAEoCVIHcGF5bG9hZA==');
diff --git a/app_dart/lib/src/model/proto/internal/github_webhook.pbserver.dart b/app_dart/lib/src/model/proto/internal/github_webhook.pbserver.dart
deleted file mode 100644
index 8e8dfb9..0000000
--- a/app_dart/lib/src/model/proto/internal/github_webhook.pbserver.dart
+++ /dev/null
@@ -1,8 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/proto/internal/github_webhook.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
-
-export 'github_webhook.pb.dart';
diff --git a/app_dart/lib/src/model/proto/internal/github_webhook.proto b/app_dart/lib/src/model/proto/internal/github_webhook.proto
deleted file mode 100644
index 5c28bef..0000000
--- a/app_dart/lib/src/model/proto/internal/github_webhook.proto
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2019 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-syntax = "proto2";
-
-package cocoon;
-
-// For full spec, see:
-// * https://docs.github.com/webhooks-and-events/webhooks/webhook-events-and-payloads
-message GithubWebhookMessage {
- // X-GitHub-Event HTTP Header indicating the webhook action.
- optional string event = 1;
- // JSON encoded webhook payload from GitHub.
- optional string payload = 2;
-}
\ No newline at end of file
diff --git a/app_dart/lib/src/model/proto/internal/key.pb.dart b/app_dart/lib/src/model/proto/internal/key.pb.dart
deleted file mode 100644
index 76d26db..0000000
--- a/app_dart/lib/src/model/proto/internal/key.pb.dart
+++ /dev/null
@@ -1,196 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/proto/internal/key.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
-
-import 'dart:core' as $core;
-
-import 'package:fixnum/fixnum.dart' as $fixnum;
-import 'package:protobuf/protobuf.dart' as $pb;
-
-enum Key_Id { uid, name, notSet }
-
-class Key extends $pb.GeneratedMessage {
- static const $core.Map<$core.int, Key_Id> _Key_IdByTag = {2: Key_Id.uid, 3: Key_Id.name, 0: Key_Id.notSet};
- static final $pb.BuilderInfo _i = $pb.BuilderInfo(
- const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Key',
- package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'cocoon'),
- createEmptyInstance: create)
- ..oo(0, [2, 3])
- ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'type')
- ..aInt64(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'uid')
- ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name')
- ..aOM<Key>(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'child', subBuilder: Key.create)
- ..hasRequiredFields = false;
-
- Key._() : super();
- factory Key({
- $core.String? type,
- $fixnum.Int64? uid,
- $core.String? name,
- Key? child,
- }) {
- final _result = create();
- if (type != null) {
- _result.type = type;
- }
- if (uid != null) {
- _result.uid = uid;
- }
- if (name != null) {
- _result.name = name;
- }
- if (child != null) {
- _result.child = child;
- }
- return _result;
- }
- factory Key.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromBuffer(i, r);
- factory Key.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromJson(i, r);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
- 'Will be removed in next major version')
- Key clone() => Key()..mergeFromMessage(this);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
- 'Will be removed in next major version')
- Key copyWith(void Function(Key) updates) =>
- super.copyWith((message) => updates(message as Key)) as Key; // ignore: deprecated_member_use
- $pb.BuilderInfo get info_ => _i;
- @$core.pragma('dart2js:noInline')
- static Key create() => Key._();
- Key createEmptyInstance() => create();
- static $pb.PbList<Key> createRepeated() => $pb.PbList<Key>();
- @$core.pragma('dart2js:noInline')
- static Key getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Key>(create);
- static Key? _defaultInstance;
-
- Key_Id whichId() => _Key_IdByTag[$_whichOneof(0)]!;
- void clearId() => clearField($_whichOneof(0));
-
- @$pb.TagNumber(1)
- $core.String get type => $_getSZ(0);
- @$pb.TagNumber(1)
- set type($core.String v) {
- $_setString(0, v);
- }
-
- @$pb.TagNumber(1)
- $core.bool hasType() => $_has(0);
- @$pb.TagNumber(1)
- void clearType() => clearField(1);
-
- @$pb.TagNumber(2)
- $fixnum.Int64 get uid => $_getI64(1);
- @$pb.TagNumber(2)
- set uid($fixnum.Int64 v) {
- $_setInt64(1, v);
- }
-
- @$pb.TagNumber(2)
- $core.bool hasUid() => $_has(1);
- @$pb.TagNumber(2)
- void clearUid() => clearField(2);
-
- @$pb.TagNumber(3)
- $core.String get name => $_getSZ(2);
- @$pb.TagNumber(3)
- set name($core.String v) {
- $_setString(2, v);
- }
-
- @$pb.TagNumber(3)
- $core.bool hasName() => $_has(2);
- @$pb.TagNumber(3)
- void clearName() => clearField(3);
-
- @$pb.TagNumber(4)
- Key get child => $_getN(3);
- @$pb.TagNumber(4)
- set child(Key v) {
- setField(4, v);
- }
-
- @$pb.TagNumber(4)
- $core.bool hasChild() => $_has(3);
- @$pb.TagNumber(4)
- void clearChild() => clearField(4);
- @$pb.TagNumber(4)
- Key ensureChild() => $_ensure(3);
-}
-
-class RootKey extends $pb.GeneratedMessage {
- static final $pb.BuilderInfo _i = $pb.BuilderInfo(
- const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'RootKey',
- package: const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'cocoon'),
- createEmptyInstance: create)
- ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'namespace')
- ..aOM<Key>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'child', subBuilder: Key.create)
- ..hasRequiredFields = false;
-
- RootKey._() : super();
- factory RootKey({
- $core.String? namespace,
- Key? child,
- }) {
- final _result = create();
- if (namespace != null) {
- _result.namespace = namespace;
- }
- if (child != null) {
- _result.child = child;
- }
- return _result;
- }
- factory RootKey.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromBuffer(i, r);
- factory RootKey.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromJson(i, r);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
- 'Will be removed in next major version')
- RootKey clone() => RootKey()..mergeFromMessage(this);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
- 'Will be removed in next major version')
- RootKey copyWith(void Function(RootKey) updates) =>
- super.copyWith((message) => updates(message as RootKey)) as RootKey; // ignore: deprecated_member_use
- $pb.BuilderInfo get info_ => _i;
- @$core.pragma('dart2js:noInline')
- static RootKey create() => RootKey._();
- RootKey createEmptyInstance() => create();
- static $pb.PbList<RootKey> createRepeated() => $pb.PbList<RootKey>();
- @$core.pragma('dart2js:noInline')
- static RootKey getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<RootKey>(create);
- static RootKey? _defaultInstance;
-
- @$pb.TagNumber(1)
- $core.String get namespace => $_getSZ(0);
- @$pb.TagNumber(1)
- set namespace($core.String v) {
- $_setString(0, v);
- }
-
- @$pb.TagNumber(1)
- $core.bool hasNamespace() => $_has(0);
- @$pb.TagNumber(1)
- void clearNamespace() => clearField(1);
-
- @$pb.TagNumber(2)
- Key get child => $_getN(1);
- @$pb.TagNumber(2)
- set child(Key v) {
- setField(2, v);
- }
-
- @$pb.TagNumber(2)
- $core.bool hasChild() => $_has(1);
- @$pb.TagNumber(2)
- void clearChild() => clearField(2);
- @$pb.TagNumber(2)
- Key ensureChild() => $_ensure(1);
-}
diff --git a/app_dart/lib/src/model/proto/internal/key.pbenum.dart b/app_dart/lib/src/model/proto/internal/key.pbenum.dart
deleted file mode 100644
index 1c5e28e..0000000
--- a/app_dart/lib/src/model/proto/internal/key.pbenum.dart
+++ /dev/null
@@ -1,6 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/proto/internal/key.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
diff --git a/app_dart/lib/src/model/proto/internal/key.pbjson.dart b/app_dart/lib/src/model/proto/internal/key.pbjson.dart
deleted file mode 100644
index 315a58e..0000000
--- a/app_dart/lib/src/model/proto/internal/key.pbjson.dart
+++ /dev/null
@@ -1,40 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/proto/internal/key.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
-
-import 'dart:core' as $core;
-import 'dart:convert' as $convert;
-import 'dart:typed_data' as $typed_data;
-
-@$core.Deprecated('Use keyDescriptor instead')
-const Key$json = const {
- '1': 'Key',
- '2': const [
- const {'1': 'type', '3': 1, '4': 1, '5': 9, '10': 'type'},
- const {'1': 'uid', '3': 2, '4': 1, '5': 3, '9': 0, '10': 'uid'},
- const {'1': 'name', '3': 3, '4': 1, '5': 9, '9': 0, '10': 'name'},
- const {'1': 'child', '3': 4, '4': 1, '5': 11, '6': '.cocoon.Key', '10': 'child'},
- ],
- '8': const [
- const {'1': 'id'},
- ],
-};
-
-/// Descriptor for `Key`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List keyDescriptor = $convert.base64Decode(
- 'CgNLZXkSEgoEdHlwZRgBIAEoCVIEdHlwZRISCgN1aWQYAiABKANIAFIDdWlkEhQKBG5hbWUYAyABKAlIAFIEbmFtZRIhCgVjaGlsZBgEIAEoCzILLmNvY29vbi5LZXlSBWNoaWxkQgQKAmlk');
-@$core.Deprecated('Use rootKeyDescriptor instead')
-const RootKey$json = const {
- '1': 'RootKey',
- '2': const [
- const {'1': 'namespace', '3': 1, '4': 1, '5': 9, '10': 'namespace'},
- const {'1': 'child', '3': 2, '4': 1, '5': 11, '6': '.cocoon.Key', '10': 'child'},
- ],
-};
-
-/// Descriptor for `RootKey`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List rootKeyDescriptor = $convert.base64Decode(
- 'CgdSb290S2V5EhwKCW5hbWVzcGFjZRgBIAEoCVIJbmFtZXNwYWNlEiEKBWNoaWxkGAIgASgLMgsuY29jb29uLktleVIFY2hpbGQ=');
diff --git a/app_dart/lib/src/model/proto/internal/key.pbserver.dart b/app_dart/lib/src/model/proto/internal/key.pbserver.dart
deleted file mode 100644
index 92721a9..0000000
--- a/app_dart/lib/src/model/proto/internal/key.pbserver.dart
+++ /dev/null
@@ -1,8 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/proto/internal/key.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
-
-export 'key.pb.dart';
diff --git a/app_dart/lib/src/model/proto/internal/key.proto b/app_dart/lib/src/model/proto/internal/key.proto
deleted file mode 100644
index 262418f..0000000
--- a/app_dart/lib/src/model/proto/internal/key.proto
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2019 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-syntax = "proto2";
-
-package cocoon;
-
-message Key {
- optional string type = 1;
-
- oneof id {
- int64 uid = 2;
- string name = 3;
- }
-
- optional Key child = 4;
-}
-
-message RootKey {
- optional string namespace = 1;
- optional Key child = 2;
-}
diff --git a/app_dart/lib/src/model/proto/internal/scheduler.pb.dart b/app_dart/lib/src/model/proto/internal/scheduler.pb.dart
deleted file mode 100644
index f79eede..0000000
--- a/app_dart/lib/src/model/proto/internal/scheduler.pb.dart
+++ /dev/null
@@ -1,437 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/proto/internal/scheduler.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
-
-import 'dart:core' as $core;
-
-import 'package:protobuf/protobuf.dart' as $pb;
-
-import 'scheduler.pbenum.dart';
-
-export 'scheduler.pbenum.dart';
-
-class SchedulerConfig_PlatformProperties extends $pb.GeneratedMessage {
- static final $pb.BuilderInfo _i = $pb.BuilderInfo(
- const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'SchedulerConfig.PlatformProperties',
- package:
- const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'scheduler'),
- createEmptyInstance: create)
- ..m<$core.String, $core.String>(
- 1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'properties',
- entryClassName: 'SchedulerConfig.PlatformProperties.PropertiesEntry',
- keyFieldType: $pb.PbFieldType.OS,
- valueFieldType: $pb.PbFieldType.OS,
- packageName: const $pb.PackageName('scheduler'))
- ..m<$core.String, $core.String>(
- 2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'dimensions',
- entryClassName: 'SchedulerConfig.PlatformProperties.DimensionsEntry',
- keyFieldType: $pb.PbFieldType.OS,
- valueFieldType: $pb.PbFieldType.OS,
- packageName: const $pb.PackageName('scheduler'))
- ..hasRequiredFields = false;
-
- SchedulerConfig_PlatformProperties._() : super();
- factory SchedulerConfig_PlatformProperties({
- $core.Map<$core.String, $core.String>? properties,
- $core.Map<$core.String, $core.String>? dimensions,
- }) {
- final _result = create();
- if (properties != null) {
- _result.properties.addAll(properties);
- }
- if (dimensions != null) {
- _result.dimensions.addAll(dimensions);
- }
- return _result;
- }
- factory SchedulerConfig_PlatformProperties.fromBuffer($core.List<$core.int> i,
- [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromBuffer(i, r);
- factory SchedulerConfig_PlatformProperties.fromJson($core.String i,
- [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromJson(i, r);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
- 'Will be removed in next major version')
- SchedulerConfig_PlatformProperties clone() => SchedulerConfig_PlatformProperties()..mergeFromMessage(this);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
- 'Will be removed in next major version')
- SchedulerConfig_PlatformProperties copyWith(void Function(SchedulerConfig_PlatformProperties) updates) =>
- super.copyWith((message) => updates(message as SchedulerConfig_PlatformProperties))
- as SchedulerConfig_PlatformProperties; // ignore: deprecated_member_use
- $pb.BuilderInfo get info_ => _i;
- @$core.pragma('dart2js:noInline')
- static SchedulerConfig_PlatformProperties create() => SchedulerConfig_PlatformProperties._();
- SchedulerConfig_PlatformProperties createEmptyInstance() => create();
- static $pb.PbList<SchedulerConfig_PlatformProperties> createRepeated() =>
- $pb.PbList<SchedulerConfig_PlatformProperties>();
- @$core.pragma('dart2js:noInline')
- static SchedulerConfig_PlatformProperties getDefault() =>
- _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<SchedulerConfig_PlatformProperties>(create);
- static SchedulerConfig_PlatformProperties? _defaultInstance;
-
- @$pb.TagNumber(1)
- $core.Map<$core.String, $core.String> get properties => $_getMap(0);
-
- @$pb.TagNumber(2)
- $core.Map<$core.String, $core.String> get dimensions => $_getMap(1);
-}
-
-class SchedulerConfig extends $pb.GeneratedMessage {
- static final $pb.BuilderInfo _i = $pb.BuilderInfo(
- const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'SchedulerConfig',
- package:
- const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'scheduler'),
- createEmptyInstance: create)
- ..pc<Target>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'targets', $pb.PbFieldType.PM,
- subBuilder: Target.create)
- ..pPS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'enabledBranches')
- ..m<$core.String, SchedulerConfig_PlatformProperties>(
- 3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'platformProperties',
- entryClassName: 'SchedulerConfig.PlatformPropertiesEntry',
- keyFieldType: $pb.PbFieldType.OS,
- valueFieldType: $pb.PbFieldType.OM,
- valueCreator: SchedulerConfig_PlatformProperties.create,
- packageName: const $pb.PackageName('scheduler'))
- ..hasRequiredFields = false;
-
- SchedulerConfig._() : super();
- factory SchedulerConfig({
- $core.Iterable<Target>? targets,
- $core.Iterable<$core.String>? enabledBranches,
- $core.Map<$core.String, SchedulerConfig_PlatformProperties>? platformProperties,
- }) {
- final _result = create();
- if (targets != null) {
- _result.targets.addAll(targets);
- }
- if (enabledBranches != null) {
- _result.enabledBranches.addAll(enabledBranches);
- }
- if (platformProperties != null) {
- _result.platformProperties.addAll(platformProperties);
- }
- return _result;
- }
- factory SchedulerConfig.fromBuffer($core.List<$core.int> i,
- [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromBuffer(i, r);
- factory SchedulerConfig.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromJson(i, r);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
- 'Will be removed in next major version')
- SchedulerConfig clone() => SchedulerConfig()..mergeFromMessage(this);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
- 'Will be removed in next major version')
- SchedulerConfig copyWith(void Function(SchedulerConfig) updates) =>
- super.copyWith((message) => updates(message as SchedulerConfig))
- as SchedulerConfig; // ignore: deprecated_member_use
- $pb.BuilderInfo get info_ => _i;
- @$core.pragma('dart2js:noInline')
- static SchedulerConfig create() => SchedulerConfig._();
- SchedulerConfig createEmptyInstance() => create();
- static $pb.PbList<SchedulerConfig> createRepeated() => $pb.PbList<SchedulerConfig>();
- @$core.pragma('dart2js:noInline')
- static SchedulerConfig getDefault() =>
- _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<SchedulerConfig>(create);
- static SchedulerConfig? _defaultInstance;
-
- @$pb.TagNumber(1)
- $core.List<Target> get targets => $_getList(0);
-
- @$pb.TagNumber(2)
- $core.List<$core.String> get enabledBranches => $_getList(1);
-
- @$pb.TagNumber(3)
- $core.Map<$core.String, SchedulerConfig_PlatformProperties> get platformProperties => $_getMap(2);
-}
-
-class Target extends $pb.GeneratedMessage {
- static final $pb.BuilderInfo _i = $pb.BuilderInfo(
- const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Target',
- package:
- const $pb.PackageName(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'scheduler'),
- createEmptyInstance: create)
- ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name')
- ..pPS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'dependencies')
- ..aOB(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'bringup')
- ..a<$core.int>(
- 4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'timeout', $pb.PbFieldType.O3,
- defaultOrMaker: 30)
- ..a<$core.String>(
- 5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'testbed', $pb.PbFieldType.OS,
- defaultOrMaker: 'linux-vm')
- ..m<$core.String, $core.String>(
- 6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'properties',
- entryClassName: 'Target.PropertiesEntry',
- keyFieldType: $pb.PbFieldType.OS,
- valueFieldType: $pb.PbFieldType.OS,
- packageName: const $pb.PackageName('scheduler'))
- ..aOS(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'builder')
- ..e<SchedulerSystem>(
- 8, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'scheduler', $pb.PbFieldType.OE,
- defaultOrMaker: SchedulerSystem.cocoon, valueOf: SchedulerSystem.valueOf, enumValues: SchedulerSystem.values)
- ..a<$core.bool>(
- 9, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'presubmit', $pb.PbFieldType.OB,
- defaultOrMaker: true)
- ..a<$core.bool>(
- 10, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'postsubmit', $pb.PbFieldType.OB,
- defaultOrMaker: true)
- ..pPS(11, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'runIf')
- ..pPS(12, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'enabledBranches')
- ..aOS(13, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'recipe')
- ..m<$core.String, $core.String>(
- 15, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'postsubmitProperties',
- entryClassName: 'Target.PostsubmitPropertiesEntry',
- keyFieldType: $pb.PbFieldType.OS,
- valueFieldType: $pb.PbFieldType.OS,
- packageName: const $pb.PackageName('scheduler'))
- ..m<$core.String, $core.String>(
- 16, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'dimensions',
- entryClassName: 'Target.DimensionsEntry',
- keyFieldType: $pb.PbFieldType.OS,
- valueFieldType: $pb.PbFieldType.OS,
- packageName: const $pb.PackageName('scheduler'))
- ..pPS(17, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'droneDimensions')
- ..pPS(18, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'runIfNot')
- ..hasRequiredFields = false;
-
- Target._() : super();
- factory Target({
- $core.String? name,
- $core.Iterable<$core.String>? dependencies,
- $core.bool? bringup,
- $core.int? timeout,
- $core.String? testbed,
- $core.Map<$core.String, $core.String>? properties,
- @$core.Deprecated('This field is deprecated.') $core.String? builder,
- SchedulerSystem? scheduler,
- $core.bool? presubmit,
- $core.bool? postsubmit,
- $core.Iterable<$core.String>? runIf,
- $core.Iterable<$core.String>? enabledBranches,
- $core.String? recipe,
- $core.Map<$core.String, $core.String>? postsubmitProperties,
- $core.Map<$core.String, $core.String>? dimensions,
- $core.Iterable<$core.String>? droneDimensions,
- $core.Iterable<$core.String>? runIfNot,
- }) {
- final _result = create();
- if (name != null) {
- _result.name = name;
- }
- if (dependencies != null) {
- _result.dependencies.addAll(dependencies);
- }
- if (bringup != null) {
- _result.bringup = bringup;
- }
- if (timeout != null) {
- _result.timeout = timeout;
- }
- if (testbed != null) {
- _result.testbed = testbed;
- }
- if (properties != null) {
- _result.properties.addAll(properties);
- }
- if (builder != null) {
- // ignore: deprecated_member_use_from_same_package
- _result.builder = builder;
- }
- if (scheduler != null) {
- _result.scheduler = scheduler;
- }
- if (presubmit != null) {
- _result.presubmit = presubmit;
- }
- if (postsubmit != null) {
- _result.postsubmit = postsubmit;
- }
- if (runIf != null) {
- _result.runIf.addAll(runIf);
- }
- if (enabledBranches != null) {
- _result.enabledBranches.addAll(enabledBranches);
- }
- if (recipe != null) {
- _result.recipe = recipe;
- }
- if (postsubmitProperties != null) {
- _result.postsubmitProperties.addAll(postsubmitProperties);
- }
- if (dimensions != null) {
- _result.dimensions.addAll(dimensions);
- }
- if (droneDimensions != null) {
- _result.droneDimensions.addAll(droneDimensions);
- }
- if (runIfNot != null) {
- _result.runIfNot.addAll(runIfNot);
- }
- return _result;
- }
- factory Target.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromBuffer(i, r);
- factory Target.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
- create()..mergeFromJson(i, r);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
- 'Will be removed in next major version')
- Target clone() => Target()..mergeFromMessage(this);
- @$core.Deprecated('Using this can add significant overhead to your binary. '
- 'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
- 'Will be removed in next major version')
- Target copyWith(void Function(Target) updates) =>
- super.copyWith((message) => updates(message as Target)) as Target; // ignore: deprecated_member_use
- $pb.BuilderInfo get info_ => _i;
- @$core.pragma('dart2js:noInline')
- static Target create() => Target._();
- Target createEmptyInstance() => create();
- static $pb.PbList<Target> createRepeated() => $pb.PbList<Target>();
- @$core.pragma('dart2js:noInline')
- static Target getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Target>(create);
- static Target? _defaultInstance;
-
- @$pb.TagNumber(1)
- $core.String get name => $_getSZ(0);
- @$pb.TagNumber(1)
- set name($core.String v) {
- $_setString(0, v);
- }
-
- @$pb.TagNumber(1)
- $core.bool hasName() => $_has(0);
- @$pb.TagNumber(1)
- void clearName() => clearField(1);
-
- @$pb.TagNumber(2)
- $core.List<$core.String> get dependencies => $_getList(1);
-
- @$pb.TagNumber(3)
- $core.bool get bringup => $_getBF(2);
- @$pb.TagNumber(3)
- set bringup($core.bool v) {
- $_setBool(2, v);
- }
-
- @$pb.TagNumber(3)
- $core.bool hasBringup() => $_has(2);
- @$pb.TagNumber(3)
- void clearBringup() => clearField(3);
-
- @$pb.TagNumber(4)
- $core.int get timeout => $_getI(3, 30);
- @$pb.TagNumber(4)
- set timeout($core.int v) {
- $_setSignedInt32(3, v);
- }
-
- @$pb.TagNumber(4)
- $core.bool hasTimeout() => $_has(3);
- @$pb.TagNumber(4)
- void clearTimeout() => clearField(4);
-
- @$pb.TagNumber(5)
- $core.String get testbed => $_getS(4, 'linux-vm');
- @$pb.TagNumber(5)
- set testbed($core.String v) {
- $_setString(4, v);
- }
-
- @$pb.TagNumber(5)
- $core.bool hasTestbed() => $_has(4);
- @$pb.TagNumber(5)
- void clearTestbed() => clearField(5);
-
- @$pb.TagNumber(6)
- $core.Map<$core.String, $core.String> get properties => $_getMap(5);
-
- @$core.Deprecated('This field is deprecated.')
- @$pb.TagNumber(7)
- $core.String get builder => $_getSZ(6);
- @$core.Deprecated('This field is deprecated.')
- @$pb.TagNumber(7)
- set builder($core.String v) {
- $_setString(6, v);
- }
-
- @$core.Deprecated('This field is deprecated.')
- @$pb.TagNumber(7)
- $core.bool hasBuilder() => $_has(6);
- @$core.Deprecated('This field is deprecated.')
- @$pb.TagNumber(7)
- void clearBuilder() => clearField(7);
-
- @$pb.TagNumber(8)
- SchedulerSystem get scheduler => $_getN(7);
- @$pb.TagNumber(8)
- set scheduler(SchedulerSystem v) {
- setField(8, v);
- }
-
- @$pb.TagNumber(8)
- $core.bool hasScheduler() => $_has(7);
- @$pb.TagNumber(8)
- void clearScheduler() => clearField(8);
-
- @$pb.TagNumber(9)
- $core.bool get presubmit => $_getB(8, true);
- @$pb.TagNumber(9)
- set presubmit($core.bool v) {
- $_setBool(8, v);
- }
-
- @$pb.TagNumber(9)
- $core.bool hasPresubmit() => $_has(8);
- @$pb.TagNumber(9)
- void clearPresubmit() => clearField(9);
-
- @$pb.TagNumber(10)
- $core.bool get postsubmit => $_getB(9, true);
- @$pb.TagNumber(10)
- set postsubmit($core.bool v) {
- $_setBool(9, v);
- }
-
- @$pb.TagNumber(10)
- $core.bool hasPostsubmit() => $_has(9);
- @$pb.TagNumber(10)
- void clearPostsubmit() => clearField(10);
-
- @$pb.TagNumber(11)
- $core.List<$core.String> get runIf => $_getList(10);
-
- @$pb.TagNumber(12)
- $core.List<$core.String> get enabledBranches => $_getList(11);
-
- @$pb.TagNumber(13)
- $core.String get recipe => $_getSZ(12);
- @$pb.TagNumber(13)
- set recipe($core.String v) {
- $_setString(12, v);
- }
-
- @$pb.TagNumber(13)
- $core.bool hasRecipe() => $_has(12);
- @$pb.TagNumber(13)
- void clearRecipe() => clearField(13);
-
- @$pb.TagNumber(15)
- $core.Map<$core.String, $core.String> get postsubmitProperties => $_getMap(13);
-
- @$pb.TagNumber(16)
- $core.Map<$core.String, $core.String> get dimensions => $_getMap(14);
-
- @$pb.TagNumber(17)
- $core.List<$core.String> get droneDimensions => $_getList(15);
-
- @$pb.TagNumber(18)
- $core.List<$core.String> get runIfNot => $_getList(16);
-}
diff --git a/app_dart/lib/src/model/proto/internal/scheduler.pbenum.dart b/app_dart/lib/src/model/proto/internal/scheduler.pbenum.dart
deleted file mode 100644
index 8e09bd7..0000000
--- a/app_dart/lib/src/model/proto/internal/scheduler.pbenum.dart
+++ /dev/null
@@ -1,33 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/proto/internal/scheduler.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
-
-// ignore_for_file: UNDEFINED_SHOWN_NAME
-import 'dart:core' as $core;
-import 'package:protobuf/protobuf.dart' as $pb;
-
-class SchedulerSystem extends $pb.ProtobufEnum {
- static const SchedulerSystem cocoon =
- SchedulerSystem._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'cocoon');
- static const SchedulerSystem luci =
- SchedulerSystem._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'luci');
- static const SchedulerSystem google_internal =
- SchedulerSystem._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'google_internal');
- static const SchedulerSystem release =
- SchedulerSystem._(4, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'release');
-
- static const $core.List<SchedulerSystem> values = <SchedulerSystem>[
- cocoon,
- luci,
- google_internal,
- release,
- ];
-
- static final $core.Map<$core.int, SchedulerSystem> _byValue = $pb.ProtobufEnum.initByValue(values);
- static SchedulerSystem? valueOf($core.int value) => _byValue[value];
-
- const SchedulerSystem._($core.int v, $core.String n) : super(v, n);
-}
diff --git a/app_dart/lib/src/model/proto/internal/scheduler.pbjson.dart b/app_dart/lib/src/model/proto/internal/scheduler.pbjson.dart
deleted file mode 100644
index 5e04860..0000000
--- a/app_dart/lib/src/model/proto/internal/scheduler.pbjson.dart
+++ /dev/null
@@ -1,186 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/proto/internal/scheduler.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
-
-import 'dart:core' as $core;
-import 'dart:convert' as $convert;
-import 'dart:typed_data' as $typed_data;
-
-@$core.Deprecated('Use schedulerSystemDescriptor instead')
-const SchedulerSystem$json = const {
- '1': 'SchedulerSystem',
- '2': const [
- const {'1': 'cocoon', '2': 1},
- const {'1': 'luci', '2': 2},
- const {'1': 'google_internal', '2': 3},
- const {'1': 'release', '2': 4},
- ],
-};
-
-/// Descriptor for `SchedulerSystem`. Decode as a `google.protobuf.EnumDescriptorProto`.
-final $typed_data.Uint8List schedulerSystemDescriptor = $convert.base64Decode(
- 'Cg9TY2hlZHVsZXJTeXN0ZW0SCgoGY29jb29uEAESCAoEbHVjaRACEhMKD2dvb2dsZV9pbnRlcm5hbBADEgsKB3JlbGVhc2UQBA==');
-@$core.Deprecated('Use schedulerConfigDescriptor instead')
-const SchedulerConfig$json = const {
- '1': 'SchedulerConfig',
- '2': const [
- const {'1': 'targets', '3': 1, '4': 3, '5': 11, '6': '.scheduler.Target', '10': 'targets'},
- const {'1': 'enabled_branches', '3': 2, '4': 3, '5': 9, '10': 'enabledBranches'},
- const {
- '1': 'platform_properties',
- '3': 3,
- '4': 3,
- '5': 11,
- '6': '.scheduler.SchedulerConfig.PlatformPropertiesEntry',
- '10': 'platformProperties'
- },
- ],
- '3': const [SchedulerConfig_PlatformPropertiesEntry$json, SchedulerConfig_PlatformProperties$json],
-};
-
-@$core.Deprecated('Use schedulerConfigDescriptor instead')
-const SchedulerConfig_PlatformPropertiesEntry$json = const {
- '1': 'PlatformPropertiesEntry',
- '2': const [
- const {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'},
- const {'1': 'value', '3': 2, '4': 1, '5': 11, '6': '.scheduler.SchedulerConfig.PlatformProperties', '10': 'value'},
- ],
- '7': const {'7': true},
-};
-
-@$core.Deprecated('Use schedulerConfigDescriptor instead')
-const SchedulerConfig_PlatformProperties$json = const {
- '1': 'PlatformProperties',
- '2': const [
- const {
- '1': 'properties',
- '3': 1,
- '4': 3,
- '5': 11,
- '6': '.scheduler.SchedulerConfig.PlatformProperties.PropertiesEntry',
- '10': 'properties'
- },
- const {
- '1': 'dimensions',
- '3': 2,
- '4': 3,
- '5': 11,
- '6': '.scheduler.SchedulerConfig.PlatformProperties.DimensionsEntry',
- '10': 'dimensions'
- },
- ],
- '3': const [
- SchedulerConfig_PlatformProperties_PropertiesEntry$json,
- SchedulerConfig_PlatformProperties_DimensionsEntry$json
- ],
-};
-
-@$core.Deprecated('Use schedulerConfigDescriptor instead')
-const SchedulerConfig_PlatformProperties_PropertiesEntry$json = const {
- '1': 'PropertiesEntry',
- '2': const [
- const {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'},
- const {'1': 'value', '3': 2, '4': 1, '5': 9, '10': 'value'},
- ],
- '7': const {'7': true},
-};
-
-@$core.Deprecated('Use schedulerConfigDescriptor instead')
-const SchedulerConfig_PlatformProperties_DimensionsEntry$json = const {
- '1': 'DimensionsEntry',
- '2': const [
- const {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'},
- const {'1': 'value', '3': 2, '4': 1, '5': 9, '10': 'value'},
- ],
- '7': const {'7': true},
-};
-
-/// Descriptor for `SchedulerConfig`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List schedulerConfigDescriptor = $convert.base64Decode(
- 'Cg9TY2hlZHVsZXJDb25maWcSKwoHdGFyZ2V0cxgBIAMoCzIRLnNjaGVkdWxlci5UYXJnZXRSB3RhcmdldHMSKQoQZW5hYmxlZF9icmFuY2hlcxgCIAMoCVIPZW5hYmxlZEJyYW5jaGVzEmMKE3BsYXRmb3JtX3Byb3BlcnRpZXMYAyADKAsyMi5zY2hlZHVsZXIuU2NoZWR1bGVyQ29uZmlnLlBsYXRmb3JtUHJvcGVydGllc0VudHJ5UhJwbGF0Zm9ybVByb3BlcnRpZXMadAoXUGxhdGZvcm1Qcm9wZXJ0aWVzRW50cnkSEAoDa2V5GAEgASgJUgNrZXkSQwoFdmFsdWUYAiABKAsyLS5zY2hlZHVsZXIuU2NoZWR1bGVyQ29uZmlnLlBsYXRmb3JtUHJvcGVydGllc1IFdmFsdWU6AjgBGtACChJQbGF0Zm9ybVByb3BlcnRpZXMSXQoKcHJvcGVydGllcxgBIAMoCzI9LnNjaGVkdWxlci5TY2hlZHVsZXJDb25maWcuUGxhdGZvcm1Qcm9wZXJ0aWVzLlByb3BlcnRpZXNFbnRyeVIKcHJvcGVydGllcxJdCgpkaW1lbnNpb25zGAIgAygLMj0uc2NoZWR1bGVyLlNjaGVkdWxlckNvbmZpZy5QbGF0Zm9ybVByb3BlcnRpZXMuRGltZW5zaW9uc0VudHJ5UgpkaW1lbnNpb25zGj0KD1Byb3BlcnRpZXNFbnRyeRIQCgNrZXkYASABKAlSA2tleRIUCgV2YWx1ZRgCIAEoCVIFdmFsdWU6AjgBGj0KD0RpbWVuc2lvbnNFbnRyeRIQCgNrZXkYASABKAlSA2tleRIUCgV2YWx1ZRgCIAEoCVIFdmFsdWU6AjgB');
-@$core.Deprecated('Use targetDescriptor instead')
-const Target$json = const {
- '1': 'Target',
- '2': const [
- const {'1': 'name', '3': 1, '4': 1, '5': 9, '10': 'name'},
- const {'1': 'dependencies', '3': 2, '4': 3, '5': 9, '10': 'dependencies'},
- const {'1': 'bringup', '3': 3, '4': 1, '5': 8, '7': 'false', '10': 'bringup'},
- const {'1': 'timeout', '3': 4, '4': 1, '5': 5, '7': '30', '10': 'timeout'},
- const {'1': 'testbed', '3': 5, '4': 1, '5': 9, '7': 'linux-vm', '10': 'testbed'},
- const {'1': 'properties', '3': 6, '4': 3, '5': 11, '6': '.scheduler.Target.PropertiesEntry', '10': 'properties'},
- const {
- '1': 'builder',
- '3': 7,
- '4': 1,
- '5': 9,
- '8': const {'3': true},
- '10': 'builder',
- },
- const {
- '1': 'scheduler',
- '3': 8,
- '4': 1,
- '5': 14,
- '6': '.scheduler.SchedulerSystem',
- '7': 'cocoon',
- '10': 'scheduler'
- },
- const {'1': 'presubmit', '3': 9, '4': 1, '5': 8, '7': 'true', '10': 'presubmit'},
- const {'1': 'postsubmit', '3': 10, '4': 1, '5': 8, '7': 'true', '10': 'postsubmit'},
- const {'1': 'run_if', '3': 11, '4': 3, '5': 9, '10': 'runIf'},
- const {'1': 'enabled_branches', '3': 12, '4': 3, '5': 9, '10': 'enabledBranches'},
- const {'1': 'recipe', '3': 13, '4': 1, '5': 9, '10': 'recipe'},
- const {
- '1': 'postsubmit_properties',
- '3': 15,
- '4': 3,
- '5': 11,
- '6': '.scheduler.Target.PostsubmitPropertiesEntry',
- '10': 'postsubmitProperties'
- },
- const {'1': 'dimensions', '3': 16, '4': 3, '5': 11, '6': '.scheduler.Target.DimensionsEntry', '10': 'dimensions'},
- const {'1': 'drone_dimensions', '3': 17, '4': 3, '5': 9, '10': 'droneDimensions'},
- const {'1': 'run_if_not', '3': 18, '4': 3, '5': 9, '10': 'runIfNot'},
- ],
- '3': const [Target_PropertiesEntry$json, Target_PostsubmitPropertiesEntry$json, Target_DimensionsEntry$json],
- '9': const [
- const {'1': 14, '2': 15},
- ],
-};
-
-@$core.Deprecated('Use targetDescriptor instead')
-const Target_PropertiesEntry$json = const {
- '1': 'PropertiesEntry',
- '2': const [
- const {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'},
- const {'1': 'value', '3': 2, '4': 1, '5': 9, '10': 'value'},
- ],
- '7': const {'7': true},
-};
-
-@$core.Deprecated('Use targetDescriptor instead')
-const Target_PostsubmitPropertiesEntry$json = const {
- '1': 'PostsubmitPropertiesEntry',
- '2': const [
- const {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'},
- const {'1': 'value', '3': 2, '4': 1, '5': 9, '10': 'value'},
- ],
- '7': const {'7': true},
-};
-
-@$core.Deprecated('Use targetDescriptor instead')
-const Target_DimensionsEntry$json = const {
- '1': 'DimensionsEntry',
- '2': const [
- const {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'},
- const {'1': 'value', '3': 2, '4': 1, '5': 9, '10': 'value'},
- ],
- '7': const {'7': true},
-};
-
-/// Descriptor for `Target`. Decode as a `google.protobuf.DescriptorProto`.
-final $typed_data.Uint8List targetDescriptor = $convert.base64Decode(
- 'CgZUYXJnZXQSEgoEbmFtZRgBIAEoCVIEbmFtZRIiCgxkZXBlbmRlbmNpZXMYAiADKAlSDGRlcGVuZGVuY2llcxIfCgdicmluZ3VwGAMgASgIOgVmYWxzZVIHYnJpbmd1cBIcCgd0aW1lb3V0GAQgASgFOgIzMFIHdGltZW91dBIiCgd0ZXN0YmVkGAUgASgJOghsaW51eC12bVIHdGVzdGJlZBJBCgpwcm9wZXJ0aWVzGAYgAygLMiEuc2NoZWR1bGVyLlRhcmdldC5Qcm9wZXJ0aWVzRW50cnlSCnByb3BlcnRpZXMSHAoHYnVpbGRlchgHIAEoCUICGAFSB2J1aWxkZXISQAoJc2NoZWR1bGVyGAggASgOMhouc2NoZWR1bGVyLlNjaGVkdWxlclN5c3RlbToGY29jb29uUglzY2hlZHVsZXISIgoJcHJlc3VibWl0GAkgASgIOgR0cnVlUglwcmVzdWJtaXQSJAoKcG9zdHN1Ym1pdBgKIAEoCDoEdHJ1ZVIKcG9zdHN1Ym1pdBIVCgZydW5faWYYCyADKAlSBXJ1bklmEikKEGVuYWJsZWRfYnJhbmNoZXMYDCADKAlSD2VuYWJsZWRCcmFuY2hlcxIWCgZyZWNpcGUYDSABKAlSBnJlY2lwZRJgChVwb3N0c3VibWl0X3Byb3BlcnRpZXMYDyADKAsyKy5zY2hlZHVsZXIuVGFyZ2V0LlBvc3RzdWJtaXRQcm9wZXJ0aWVzRW50cnlSFHBvc3RzdWJtaXRQcm9wZXJ0aWVzEkEKCmRpbWVuc2lvbnMYECADKAsyIS5zY2hlZHVsZXIuVGFyZ2V0LkRpbWVuc2lvbnNFbnRyeVIKZGltZW5zaW9ucxIpChBkcm9uZV9kaW1lbnNpb25zGBEgAygJUg9kcm9uZURpbWVuc2lvbnMSHAoKcnVuX2lmX25vdBgSIAMoCVIIcnVuSWZOb3QaPQoPUHJvcGVydGllc0VudHJ5EhAKA2tleRgBIAEoCVIDa2V5EhQKBXZhbHVlGAIgASgJUgV2YWx1ZToCOAEaRwoZUG9zdHN1Ym1pdFByb3BlcnRpZXNFbnRyeRIQCgNrZXkYASABKAlSA2tleRIUCgV2YWx1ZRgCIAEoCVIFdmFsdWU6AjgBGj0KD0RpbWVuc2lvbnNFbnRyeRIQCgNrZXkYASABKAlSA2tleRIUCgV2YWx1ZRgCIAEoCVIFdmFsdWU6AjgBSgQIDhAP');
diff --git a/app_dart/lib/src/model/proto/internal/scheduler.pbserver.dart b/app_dart/lib/src/model/proto/internal/scheduler.pbserver.dart
deleted file mode 100644
index a16fa83..0000000
--- a/app_dart/lib/src/model/proto/internal/scheduler.pbserver.dart
+++ /dev/null
@@ -1,8 +0,0 @@
-///
-// Generated code. Do not modify.
-// source: lib/src/model/proto/internal/scheduler.proto
-//
-// @dart = 2.12
-// ignore_for_file: annotate_overrides,camel_case_types,constant_identifier_names,deprecated_member_use_from_same_package,directives_ordering,library_prefixes,non_constant_identifier_names,prefer_final_fields,return_of_invalid_type,unnecessary_const,unnecessary_import,unnecessary_this,unused_import,unused_shown_name
-
-export 'scheduler.pb.dart';
diff --git a/app_dart/lib/src/model/proto/internal/scheduler.proto b/app_dart/lib/src/model/proto/internal/scheduler.proto
deleted file mode 100644
index a0df483..0000000
--- a/app_dart/lib/src/model/proto/internal/scheduler.proto
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 2019 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-syntax = "proto2";
-
-package scheduler;
-
-// Model of .ci.yaml.
-// Next ID: 4
-message SchedulerConfig {
- // Targets to run from this config.
- repeated Target targets = 1;
- // Git branches to run these targets against.
- repeated string enabled_branches = 2;
- // Universal platform args passed to LUCI builders.
- // Keys are the platforms and values are the PlatformProperties (properties, dimensions etc.).
- map<string, PlatformProperties> platform_properties = 3;
- // Next ID: 3
- message PlatformProperties {
- // Generic key, value pairs to set platform-wide properties
- map<string, string> properties = 1;
- // Generic key, value pairs to set platform-wide dimensions
- // Doc for dimension and properties: https://chromium.googlesource.com/infra/luci/luci-py/+/HEAD/appengine/swarming/doc/User-Guide.md
- map<string, string> dimensions = 2;
- }
-}
-
-// A unit of work for infrastructure to run.
-// Next ID: 17
-message Target {
- // Unique, human readable identifier.
- optional string name = 1;
- // Names of other targets required to succeed before triggering this target.
- repeated string dependencies = 2;
- // Whether this target is stable and can be used to gate commits.
- // Defaults to false which blocks builds and does not run in presubmit.
- optional bool bringup = 3 [default = false];
- // Number of minutes this target is allowed to run before being marked as failed.
- optional int32 timeout = 4 [default = 30];
- // Name of the testbed this target will run on.
- // Defaults to a linux vm.
- optional string testbed = 5 [default = 'linux-vm'];
- // Properties to configure infrastructure tooling.
- map<string, string> properties = 6;
- // Name of the LUCI builder to trigger.
- optional string builder = 7 [deprecated = true];
- // Name of the scheduler to trigger this target.
- // Defaults to being triggered by cocoon.
- optional SchedulerSystem scheduler = 8 [default = cocoon];
- // Whether target should run pre-submit. Defaults to true, will run in presubmit.
- optional bool presubmit = 9 [default = true];
- // Whether target should run post-submit. Defaults to true, will run in postsubmit.
- optional bool postsubmit = 10 [default = true];
- // List of paths that trigger this target in presubmit when there is a diff.
- // If no paths are given, it will always run.
- repeated string run_if = 11;
- // Override of enabled_branches for this target (for release targets).
- repeated string enabled_branches = 12;
- // Name of the LUCI recipe to use for the builder.
- optional string recipe = 13;
- reserved 14; // tags
- // Properties to configure infrastructure tooling for only postsubmit runs.
- map<string, string> postsubmit_properties = 15;
- // Dimensions to configure swarming dimensions of LUCI builds.
- map<string, string> dimensions = 16;
- // Dimensions used when this build runs within a drone.
- repeated string drone_dimensions = 17;
- // Runs the target if files are not in these paths.
- repeated string run_if_not = 18;
-}
-
-// Schedulers supported in SchedulerConfig.
-// Next ID: 5
-enum SchedulerSystem {
- // Cocoon will handle all actions for the target (initial trigger, retries).
- cocoon = 1;
- // LUCI triggers the build when mirrored to GoB. Cocoon triggers retries.
- luci = 2;
- // Google internally uses Flutter, and validates if tip-of-tree causes breakages.
- google_internal = 3;
- // Special Cocoon scheduler case to trigger targets intended for beta and stable releases.
- release = 4;
-}
diff --git a/app_dart/lib/src/model/proto/protos.dart b/app_dart/lib/src/model/proto/protos.dart
deleted file mode 100644
index ae7cdee..0000000
--- a/app_dart/lib/src/model/proto/protos.dart
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright 2019 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-export 'internal/build_status_response.pb.dart';
-export 'internal/github_webhook.pb.dart';
-export 'internal/key.pb.dart';
-export 'internal/scheduler.pb.dart';
diff --git a/app_dart/lib/src/request_handlers/check_flaky_builders.dart b/app_dart/lib/src/request_handlers/check_flaky_builders.dart
deleted file mode 100644
index 41d64ba..0000000
--- a/app_dart/lib/src/request_handlers/check_flaky_builders.dart
+++ /dev/null
@@ -1,229 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-
-import 'package:cocoon_service/ci_yaml.dart';
-import 'package:collection/collection.dart';
-import 'package:github/github.dart';
-import 'package:meta/meta.dart';
-import 'package:yaml/yaml.dart';
-
-import '../../protos.dart' as pb;
-import '../foundation/utils.dart';
-import '../request_handling/api_request_handler.dart';
-import '../request_handling/body.dart';
-import '../service/bigquery.dart';
-import '../service/config.dart';
-import '../service/github_service.dart';
-import 'flaky_handler_utils.dart';
-
-/// A handler to deflake builders if the builders are no longer flaky.
-///
-/// This handler gets flaky builders from ci.yaml in flutter/flutter and check
-/// the following conditions:
-/// 1. The builder is not in [ignoredBuilders].
-/// 2. The flaky issue of the builder is closed if there is one.
-/// 3. Does not have any existing pr against the target.
-/// 4. The builder has been passing for most recent [kRecordNumber] consecutive
-/// runs.
-/// 5. The builder is not marked with ignore_flakiness.
-///
-/// If all the conditions are true, this handler will file a pull request to
-/// make the builder unflaky.
-@immutable
-class CheckFlakyBuilders extends ApiRequestHandler<Body> {
- const CheckFlakyBuilders({
- required super.config,
- required super.authenticationProvider,
- });
-
- static int kRecordNumber = 50;
-
- static final RegExp _issueLinkRegex = RegExp(r'https://github.com/flutter/flutter/issues/(?<id>[0-9]+)');
-
- /// Builders that are purposefully marked flaky and should be ignored by this
- /// handler.
- static const Set<String> ignoredBuilders = <String>{
- 'Mac_ios32 flutter_gallery__transition_perf_e2e_ios32',
- 'Mac_ios32 native_ui_tests_ios',
- };
-
- @override
- Future<Body> get() async {
- final RepositorySlug slug = Config.flutterSlug;
- final GithubService gitHub = config.createGithubServiceWithToken(await config.githubOAuthToken);
- final BigqueryService bigquery = await config.createBigQueryService();
- final String ciContent = await gitHub.getFileContent(
- slug,
- kCiYamlPath,
- );
- final YamlMap? ci = loadYaml(ciContent) as YamlMap?;
- final pb.SchedulerConfig unCheckedSchedulerConfig = pb.SchedulerConfig()..mergeFromProto3Json(ci);
- final CiYaml ciYaml = CiYaml(
- slug: slug,
- branch: Config.defaultBranch(slug),
- config: unCheckedSchedulerConfig,
- );
-
- final pb.SchedulerConfig schedulerConfig = ciYaml.config;
- final List<pb.Target> targets = schedulerConfig.targets;
-
- final List<_BuilderInfo> eligibleBuilders =
- await _getEligibleFlakyBuilders(gitHub, slug, content: ciContent, ciYaml: ciYaml);
- final String testOwnerContent = await gitHub.getFileContent(
- slug,
- kTestOwnerPath,
- );
-
- for (final _BuilderInfo info in eligibleBuilders) {
- final BuilderType type = getTypeForBuilder(info.name, ciYaml);
- final TestOwnership testOwnership = getTestOwnership(
- targets.singleWhere((element) => element.name == info.name!),
- type,
- testOwnerContent,
- );
- final List<BuilderRecord> builderRecords =
- await bigquery.listRecentBuildRecordsForBuilder(kBigQueryProjectId, builder: info.name, limit: kRecordNumber);
- if (_shouldDeflake(builderRecords)) {
- await _deflakyPullRequest(gitHub, slug, info: info, ciContent: ciContent, testOwnership: testOwnership);
- // Manually add a 1s delay between consecutive GitHub requests to deal with secondary rate limit error.
- // https://docs.github.com/en/rest/guides/best-practices-for-integrators#dealing-with-secondary-rate-limits
- await Future.delayed(config.githubRequestDelay);
- }
- }
- return Body.forJson(const <String, dynamic>{
- 'Status': 'success',
- });
- }
-
- /// A builder should be deflaked if satisfying three conditions.
- /// 1) There are enough data records.
- /// 2) There is no flake
- /// 3) There is no failure
- bool _shouldDeflake(List<BuilderRecord> builderRecords) {
- return builderRecords.length >= kRecordNumber &&
- builderRecords.every((BuilderRecord record) => !record.isFlaky && !record.isFailed);
- }
-
- /// Gets the builders that match conditions:
- /// 1. The builder's ignoreFlakiness is false.
- /// 2. The builder is flaky
- /// 3. The builder is not in [ignoredBuilders].
- /// 4. The flaky issue of the builder is closed if there is one.
- /// 5. Does not have any existing pr against the builder.
- Future<List<_BuilderInfo>> _getEligibleFlakyBuilders(
- GithubService gitHub,
- RepositorySlug slug, {
- required String content,
- required CiYaml ciYaml,
- }) async {
- final YamlMap ci = loadYaml(content) as YamlMap;
- final YamlList targets = ci[kCiYamlTargetsKey] as YamlList;
- final List<YamlMap?> flakyTargets = targets
- .where((dynamic target) => target[kCiYamlTargetIsFlakyKey] == true)
- .map<YamlMap?>((dynamic target) => target as YamlMap?)
- .toList();
- final List<_BuilderInfo> result = <_BuilderInfo>[];
- final List<String> lines = content.split('\n');
- final Map<String?, PullRequest> nameToExistingPRs = await getExistingPRs(gitHub, slug);
- for (final YamlMap? flakyTarget in flakyTargets) {
- final String? builder = flakyTarget![kCiYamlTargetNameKey] as String?;
- // If target specified ignore_flakiness, then skip.
- if (getIgnoreFlakiness(builder, ciYaml)) {
- continue;
- }
- if (ignoredBuilders.contains(builder)) {
- continue;
- }
- // Skip the flaky target if the issue or pr for the flaky target is still
- // open.
- if (nameToExistingPRs.containsKey(builder)) {
- continue;
- }
-
- //TODO (ricardoamador): Refactor this so we don't need to parse the entire yaml looking for commented issues, https://github.com/flutter/flutter/issues/113232
- int builderLineNumber = lines.indexWhere((String line) => line.contains('name: $builder')) + 1;
- while (builderLineNumber < lines.length && !lines[builderLineNumber].contains('name:')) {
- if (lines[builderLineNumber].contains('$kCiYamlTargetIsFlakyKey:')) {
- final RegExpMatch? match = _issueLinkRegex.firstMatch(lines[builderLineNumber]);
- if (match == null) {
- result.add(_BuilderInfo(name: builder));
- break;
- }
- final Issue issue = await gitHub.getIssue(slug, issueNumber: int.parse(match.namedGroup('id')!))!;
- if (issue.isClosed) {
- result.add(_BuilderInfo(name: builder, existingIssue: issue));
- }
- break;
- }
- builderLineNumber += 1;
- }
- }
- return result;
- }
-
- @visibleForTesting
- static bool getIgnoreFlakiness(String? builderName, CiYaml ciYaml) {
- final Target? target =
- ciYaml.postsubmitTargets.singleWhereOrNull((Target target) => target.value.name == builderName);
- return target == null ? false : target.getIgnoreFlakiness();
- }
-
- Future<void> _deflakyPullRequest(
- GithubService gitHub,
- RepositorySlug slug, {
- required _BuilderInfo info,
- required String ciContent,
- required TestOwnership testOwnership,
- }) async {
- final String modifiedContent = _deflakeBuilderInContent(ciContent, info.name);
- final GitReference masterRef = await gitHub.getReference(slug, kMasterRefs);
- final DeflakePullRequestBuilder prBuilder = DeflakePullRequestBuilder(
- name: info.name,
- recordNumber: kRecordNumber,
- ownership: testOwnership,
- issue: info.existingIssue,
- );
- final PullRequest pullRequest = await gitHub.createPullRequest(
- slug,
- title: prBuilder.pullRequestTitle,
- body: prBuilder.pullRequestBody,
- commitMessage: prBuilder.pullRequestTitle,
- baseRef: masterRef,
- entries: <CreateGitTreeEntry>[
- CreateGitTreeEntry(
- kCiYamlPath,
- kModifyMode,
- kModifyType,
- content: modifiedContent,
- ),
- ],
- );
- await gitHub.assignReviewer(slug, reviewer: prBuilder.pullRequestReviewer, pullRequestNumber: pullRequest.number);
- }
-
- /// Removes the `bringup: true` for the builder in the ci.yaml.
- String _deflakeBuilderInContent(String content, String? builder) {
- final List<String> lines = content.split('\n');
- final int builderLineNumber = lines.indexWhere((String line) => line.contains('name: $builder'));
- int nextLine = builderLineNumber + 1;
- while (nextLine < lines.length && !lines[nextLine].contains('name:')) {
- if (lines[nextLine].contains('$kCiYamlTargetIsFlakyKey:')) {
- lines.removeAt(nextLine);
- return lines.join('\n');
- }
- nextLine += 1;
- }
- throw 'Cannot find the flaky flag, is the test really marked flaky?';
- }
-}
-
-/// The info of the builder's name and if there is any existing issue opened
-/// for the builder.
-class _BuilderInfo {
- _BuilderInfo({this.name, this.existingIssue});
- final String? name;
- final Issue? existingIssue;
-}
diff --git a/app_dart/lib/src/request_handlers/create_branch.dart b/app_dart/lib/src/request_handlers/create_branch.dart
deleted file mode 100644
index 7d0d688..0000000
--- a/app_dart/lib/src/request_handlers/create_branch.dart
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2020 The Flutter 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:async';
-
-import '../request_handling/api_request_handler.dart';
-import '../request_handling/body.dart';
-import '../service/branch_service.dart';
-
-/// Creates the flutter/recipes branch to match a flutter/flutter branch.
-///
-/// This is used by Google Testing to create release infra whenever a good
-/// commit has been found, and is being considered as the branch point to
-/// be rolled into Google.
-class CreateBranch extends ApiRequestHandler<Body> {
- const CreateBranch({
- required this.branchService,
- required super.config,
- required super.authenticationProvider,
- });
-
- final BranchService branchService;
-
- static const String branchParam = 'branch';
- static const String engineShaParam = 'engine';
-
- @override
- Future<Body> get() async {
- checkRequiredQueryParameters(<String>[branchParam, engineShaParam]);
- final String branch = request!.uri.queryParameters[branchParam]!;
- final String engineSha = request!.uri.queryParameters[engineShaParam]!;
-
- await branchService.branchFlutterRecipes(branch, engineSha);
-
- return Body.empty;
- }
-}
diff --git a/app_dart/lib/src/request_handlers/dart_internal_subscription.dart b/app_dart/lib/src/request_handlers/dart_internal_subscription.dart
deleted file mode 100644
index b778c5d..0000000
--- a/app_dart/lib/src/request_handlers/dart_internal_subscription.dart
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright 2023 The Flutter 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:convert';
-
-import 'package:cocoon_service/src/model/luci/buildbucket.dart';
-import 'package:meta/meta.dart';
-
-import '../../cocoon_service.dart';
-import '../model/appengine/task.dart';
-import '../request_handling/subscription_handler.dart';
-import '../service/datastore.dart';
-import '../service/logging.dart';
-
-/// TODO(drewroengoogle): Make this subscription generic so we can accept more
-/// than just dart-internal builds.
-///
-/// An endpoint for listening to build updates for dart-internal builds and
-/// saving the results to the datastore.
-///
-/// The PubSub subscription is set up here:
-/// https://console.cloud.google.com/cloudpubsub/subscription/detail/dart-internal-build-results-sub?project=flutter-dashboard
-@immutable
-class DartInternalSubscription extends SubscriptionHandler {
- /// Creates an endpoint for listening for dart-internal build results.
- /// The message should contain a single buildbucket id
- const DartInternalSubscription({
- required super.cache,
- required super.config,
- super.authProvider,
- required this.buildBucketClient,
- @visibleForTesting this.datastoreProvider = DatastoreService.defaultProvider,
- }) : super(subscriptionName: 'dart-internal-build-results-sub');
-
- final BuildBucketClient buildBucketClient;
- final DatastoreServiceProvider datastoreProvider;
-
- @override
- Future<Body> post() async {
- final DatastoreService datastore = datastoreProvider(config.db);
-
- if (message.data == null) {
- log.info('no data in message');
- return Body.empty;
- }
-
- final dynamic buildData = json.decode(message.data!);
- log.info('Build data json: $buildData');
-
- if (buildData['build'] == null) {
- log.info('no build information in message');
- return Body.empty;
- }
-
- final String project = buildData['build']['builder']['project'];
- final String bucket = buildData['build']['builder']['bucket'];
- final String builder = buildData['build']['builder']['builder'];
-
- // This should already be covered by the pubsub filter, but adding an additional check
- // to ensure we don't process builds that aren't from dart-internal/flutter.
- if (project != 'dart-internal' || bucket != 'flutter') {
- log.info('Ignoring build not from dart-internal/flutter bucket');
- return Body.empty;
- }
-
- // Only publish the parent release_builder builds to the datastore.
- // TODO(drewroengoogle): Remove this regex in favor of supporting *all* dart-internal build results.
- // Issue: https://github.com/flutter/flutter/issues/134674
- final regex =
- RegExp(r'(Linux|Mac|Windows)\s+(engine_release_builder|packaging_release_builder|flutter_release_builder)');
- if (!regex.hasMatch(builder)) {
- log.info('Ignoring builder that is not a release builder');
- return Body.empty;
- }
-
- final String buildbucketId = buildData['build']['id'];
- log.info('Creating build request object with build id $buildbucketId');
- final GetBuildRequest request = GetBuildRequest(
- id: buildbucketId,
- );
-
- log.info(
- 'Calling buildbucket api to get build data for build $buildbucketId',
- );
- final Build build = await buildBucketClient.getBuild(request);
-
- log.info('Checking for existing task in datastore');
- final Task? existingTask = await datastore.getTaskFromBuildbucketBuild(build);
-
- late Task taskToInsert;
- if (existingTask != null) {
- log.info('Updating Task from existing Task');
- existingTask.updateFromBuildbucketBuild(build);
- taskToInsert = existingTask;
- } else {
- log.info('Creating Task from Buildbucket result');
- taskToInsert = await Task.fromBuildbucketBuild(build, datastore);
- }
-
- log.info('Inserting Task into the datastore: ${taskToInsert.toString()}');
- await datastore.insert(<Task>[taskToInsert]);
-
- return Body.forJson(taskToInsert.toString());
- }
-}
diff --git a/app_dart/lib/src/request_handlers/file_flaky_issue_and_pr.dart b/app_dart/lib/src/request_handlers/file_flaky_issue_and_pr.dart
deleted file mode 100644
index 10ab0bc..0000000
--- a/app_dart/lib/src/request_handlers/file_flaky_issue_and_pr.dart
+++ /dev/null
@@ -1,206 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-
-import 'package:cocoon_service/ci_yaml.dart';
-import 'package:collection/collection.dart';
-import 'package:github/github.dart';
-import 'package:meta/meta.dart';
-import 'package:yaml/yaml.dart';
-
-import '../../protos.dart' as pb;
-import '../foundation/utils.dart';
-import '../request_handling/api_request_handler.dart';
-import '../request_handling/body.dart';
-import '../service/bigquery.dart';
-import '../service/config.dart';
-import '../service/github_service.dart';
-import 'flaky_handler_utils.dart';
-
-/// A handler that queries build statistics from luci and file issues and pull
-/// requests for tests that have high flaky ratios.
-///
-/// The query parameter kThresholdKey is required for this handler to use it as
-/// the standard when compares the flaky ratios.
-@immutable
-class FileFlakyIssueAndPR extends ApiRequestHandler<Body> {
- const FileFlakyIssueAndPR({
- required super.config,
- required super.authenticationProvider,
- });
-
- static const String kThresholdKey = 'threshold';
-
- @override
- Future<Body> get() async {
- final RepositorySlug slug = Config.flutterSlug;
- final GithubService gitHub = config.createGithubServiceWithToken(await config.githubOAuthToken);
- final BigqueryService bigquery = await config.createBigQueryService();
- final List<BuilderStatistic> builderStatisticList = await bigquery.listBuilderStatistic(kBigQueryProjectId);
- final YamlMap? ci = loadYaml(await gitHub.getFileContent(slug, kCiYamlPath)) as YamlMap?;
- final pb.SchedulerConfig unCheckedSchedulerConfig = pb.SchedulerConfig()..mergeFromProto3Json(ci);
- final CiYaml ciYaml = CiYaml(
- slug: slug,
- branch: Config.defaultBranch(slug),
- config: unCheckedSchedulerConfig,
- );
-
- final pb.SchedulerConfig schedulerConfig = ciYaml.config;
- final List<pb.Target> targets = schedulerConfig.targets;
-
- final String testOwnerContent = await gitHub.getFileContent(slug, kTestOwnerPath);
- final Map<String?, Issue> nameToExistingIssue = await getExistingIssues(gitHub, slug);
- final Map<String?, PullRequest> nameToExistingPR = await getExistingPRs(gitHub, slug);
- int filedIssueAndPRCount = 0;
- for (final BuilderStatistic statistic in builderStatisticList) {
- // Skip if ignore_flakiness is specified.
- if (getIgnoreFlakiness(statistic.name, ciYaml)) {
- continue;
- }
- if (statistic.flakyRate < _threshold) {
- continue;
- }
-
- final BuilderType type = getTypeForBuilder(statistic.name, ciYaml);
- final bool issueAndPRFiled = await _fileIssueAndPR(
- gitHub,
- slug,
- builderDetail: BuilderDetail(
- statistic: statistic,
- existingIssue: nameToExistingIssue[statistic.name],
- existingPullRequest: nameToExistingPR[statistic.name],
- isMarkedFlaky: _getIsMarkedFlaky(statistic.name, ci!),
- type: type,
- ownership: getTestOwnership(
- targets.singleWhere((element) => element.name == statistic.name),
- type,
- testOwnerContent,
- ),
- ),
- );
- if (issueAndPRFiled) {
- filedIssueAndPRCount++;
- }
- if (filedIssueAndPRCount == config.issueAndPRLimit) {
- break;
- }
- }
- return Body.forJson(<String, dynamic>{
- 'Status': 'success',
- 'NumberOfCreatedIssuesAndPRs': filedIssueAndPRCount,
- });
- }
-
- double get _threshold => double.parse(request!.uri.queryParameters[kThresholdKey]!);
-
- Future<bool> _fileIssueAndPR(
- GithubService gitHub,
- RepositorySlug slug, {
- required BuilderDetail builderDetail,
- }) async {
- Issue? issue = builderDetail.existingIssue;
- if (_shouldNotFileIssueAndPR(builderDetail, issue)) {
- return false;
- }
- // Manually add a 1s delay between consecutive GitHub requests to deal with secondary rate limit error.
- // https://docs.github.com/en/rest/guides/best-practices-for-integrators#dealing-with-secondary-rate-limits
- await Future.delayed(config.githubRequestDelay);
- issue = await fileFlakyIssue(builderDetail: builderDetail, gitHub: gitHub, slug: slug, threshold: _threshold);
-
- if (builderDetail.type == BuilderType.shard ||
- builderDetail.type == BuilderType.unknown ||
- builderDetail.existingPullRequest != null) {
- return true;
- }
- final String modifiedContent = _marksBuildFlakyInContent(
- await gitHub.getFileContent(
- slug,
- kCiYamlPath,
- ),
- builderDetail.statistic.name,
- issue.htmlUrl,
- );
- final GitReference masterRef = await gitHub.getReference(slug, kMasterRefs);
- final PullRequestBuilder prBuilder =
- PullRequestBuilder(statistic: builderDetail.statistic, ownership: builderDetail.ownership, issue: issue);
- final PullRequest pullRequest = await gitHub.createPullRequest(
- slug,
- title: prBuilder.pullRequestTitle,
- body: prBuilder.pullRequestBody,
- commitMessage: prBuilder.pullRequestTitle,
- baseRef: masterRef,
- entries: <CreateGitTreeEntry>[
- CreateGitTreeEntry(
- kCiYamlPath,
- kModifyMode,
- kModifyType,
- content: modifiedContent,
- ),
- ],
- );
- final String? label = getTeamLabelFromTeam(builderDetail.ownership.team);
- await gitHub.assignReviewer(slug, reviewer: prBuilder.pullRequestReviewer, pullRequestNumber: pullRequest.number);
- if (label != null) {
- await gitHub.addIssueLabels(slug, pullRequest.number!, <String>[label]);
- }
- return true;
- }
-
- bool _shouldNotFileIssueAndPR(BuilderDetail builderDetail, Issue? issue) {
- // Don't create a new issue or deflake PR using prod builds statuses if the builder has been marked as flaky.
- // If the builder is `bringup: true`, but still hit flakes, a new bug will be filed in `/api/check_flaky_builders`
- // based on staging builds statuses.
- if (builderDetail.isMarkedFlaky) {
- return true;
- }
-
- // Don't create a new issue or deflake PR if there is an open issue or a recent closed
- // issue within kGracePeriodForClosedFlake days. It takes time for the flaky ratio to go
- // down after the fix is merged.
- if (issue != null &&
- (issue.state != 'closed' ||
- DateTime.now().difference(issue.closedAt!) <= const Duration(days: kGracePeriodForClosedFlake))) {
- return true;
- }
-
- return false;
- }
-
- bool _getIsMarkedFlaky(String builderName, YamlMap ci) {
- final YamlList targets = ci[kCiYamlTargetsKey] as YamlList;
- final YamlMap? target = targets.firstWhere(
- (dynamic element) => element[kCiYamlTargetNameKey] == builderName,
- orElse: () => null,
- ) as YamlMap?;
- return target != null && target[kCiYamlTargetIsFlakyKey] == true;
- }
-
- @visibleForTesting
- static bool getIgnoreFlakiness(String builderName, CiYaml ciYaml) {
- final Target? target =
- ciYaml.postsubmitTargets.singleWhereOrNull((Target target) => target.value.name == builderName);
- return target == null ? false : target.getIgnoreFlakiness();
- }
-
- String _marksBuildFlakyInContent(String content, String builder, String issueUrl) {
- final List<String> lines = content.split('\n');
- final int builderLineNumber = lines.indexWhere((String line) => line.contains('name: $builder'));
- // Takes care the case if is kCiYamlTargetIsFlakyKey is already defined to false
- int nextLine = builderLineNumber + 1;
- while (nextLine < lines.length && !lines[nextLine].contains('name:')) {
- if (lines[nextLine].contains('$kCiYamlTargetIsFlakyKey:')) {
- lines[nextLine] = lines[nextLine].replaceFirst('false', 'true # Flaky $issueUrl');
- return lines.join('\n');
- }
- nextLine += 1;
- }
- lines.insert(builderLineNumber + 1, ' $kCiYamlTargetIsFlakyKey: true # Flaky $issueUrl');
- return lines.join('\n');
- }
-
- Future<RepositorySlug> getSlugFor(GitHub client, String repository) async {
- return RepositorySlug((await client.users.getCurrentUser()).login!, repository);
- }
-}
diff --git a/app_dart/lib/src/request_handlers/flaky_handler_utils.dart b/app_dart/lib/src/request_handlers/flaky_handler_utils.dart
deleted file mode 100644
index a5f31df..0000000
--- a/app_dart/lib/src/request_handlers/flaky_handler_utils.dart
+++ /dev/null
@@ -1,480 +0,0 @@
-// Copyright 2019 The Flutter 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:convert';
-import 'dart:core';
-
-import 'package:cocoon_service/ci_yaml.dart';
-import 'package:cocoon_service/src/request_handlers/test_ownership.dart';
-import 'package:collection/collection.dart';
-import 'package:github/github.dart';
-
-import '../service/bigquery.dart';
-import '../service/github_service.dart';
-import '../../protos.dart' as pb;
-
-// String constants.
-const String kFlakeLabel = 'c: flake';
-const String kFrameworkLabel = 'team-framework';
-const String kToolLabel = 'team-tool';
-const String kEngineLabel = 'team-engine';
-const String kWebLabel = 'team-web';
-const String kInfraLabel = 'team-infra';
-const String kAndroidLabel = 'team-android';
-const String kIosLabel = 'team-ios';
-const String kReleaseLabel = 'team-release';
-const String kEcosystemLabel = 'team-ecosystem';
-const String kP0Label = 'P0';
-const String kP1Label = 'P1';
-const String kP2Label = 'P2';
-const String kP3Label = 'P3';
-
-const String kBigQueryProjectId = 'flutter-dashboard';
-const String kCiYamlTargetsKey = 'targets';
-const String kCiYamlTargetNameKey = 'name';
-const String kCiYamlTargetIgnoreFlakiness = 'ignore_flakiness';
-const String kCiYamlTargetIsFlakyKey = 'bringup';
-const String kCiYamlPropertiesKey = 'properties';
-const String kCiYamlTargetTagsKey = 'tags';
-const String kCiYamlTargetTagsShard = 'shard';
-const String kCiYamlTargetTagsFirebaselab = 'firebaselab';
-const String kCiYamlTargetTagsDevicelab = 'devicelab';
-const String kCiYamlTargetTagsFramework = 'framework';
-const String kCiYamlTargetTagsHostonly = 'hostonly';
-
-const String kMasterRefs = 'heads/master';
-const String kModifyMode = '100644'; // This is equivalent to mode: `-rw-r--r--`.
-const String kModifyType = 'blob';
-
-const int kSuccessBuildNumberLimit = 3;
-const int kFlayRatioBuildNumberList = 10;
-const double kDefaultFlakyRatioThreshold = 0.02;
-const int kGracePeriodForClosedFlake = 15; // days
-
-const String _commitPrefix = 'https://github.com/flutter/flutter/commit/';
-const String _buildDashboardPrefix = 'https://flutter-dashboard.appspot.com/#/build';
-const String _prodBuildPrefix = 'https://ci.chromium.org/ui/p/flutter/builders/prod/';
-const String _stagingBuildPrefix = 'https://ci.chromium.org/ui/p/flutter/builders/staging/';
-const String _flakeRecordPrefix =
- 'https://data.corp.google.com/sites/flutter_infra_metrics_datasite/flutter_check_test_flakiness_status_dashboard/?p=BUILDER_NAME:';
-
-/// A builder to build a new issue for a flake.
-class IssueBuilder {
- IssueBuilder({
- required this.statistic,
- required this.ownership,
- required this.threshold,
- this.bringup = false,
- });
-
- final BuilderStatistic statistic;
- final TestOwnership ownership;
- final double threshold;
- final bool bringup;
-
- Bucket get buildBucket {
- return bringup ? Bucket.staging : Bucket.prod;
- }
-
- String get issueTitle {
- return '${statistic.name} is ${_formatRate(statistic.flakyRate)}% flaky';
- }
-
- String? get issueAssignee {
- return ownership.owner;
- }
-
- /// Return `kSuccessBuildNumberLimit` successful builds if there are more. Otherwise return what's available.
- int numberOfSuccessBuilds(int numberOfAvailableSuccessBuilds) {
- return numberOfAvailableSuccessBuilds >= kSuccessBuildNumberLimit
- ? kSuccessBuildNumberLimit
- : numberOfAvailableSuccessBuilds;
- }
-
- String get issueBody {
- return '''
-${_buildHiddenMetaTags(name: statistic.name)}
-${_issueSummary(statistic, threshold, bringup)}
-
-One recent flaky example for a same commit: ${_issueBuildLink(builder: statistic.name, build: statistic.flakyBuildOfRecentCommit, bucket: buildBucket)}
-Commit: $_commitPrefix${statistic.recentCommit}
-
-Flaky builds:
-${_issueBuildLinks(builder: statistic.name, builds: statistic.flakyBuilds!, bucket: buildBucket)}
-
-Recent test runs:
-${_issueBuilderLink(statistic.name)}
-
-Please follow https://github.com/flutter/flutter/wiki/Reducing-Test-Flakiness#fixing-flaky-tests to fix the flakiness and enable the test back after validating the fix (internal dashboard to validate: go/flutter_test_flakiness).
-''';
- }
-
- List<String> get issueLabels {
- final List<String> labels = <String>[
- kFlakeLabel,
- kP0Label,
- ];
- final String? teamLabel = getTeamLabelFromTeam(ownership.team);
- if (teamLabel != null && teamLabel.isNotEmpty == true) {
- labels.add(teamLabel);
- }
- return labels;
- }
-}
-
-/// A builder to build the update comment and labels for an existing open flaky
-/// issue.
-class IssueUpdateBuilder {
- IssueUpdateBuilder({
- required this.statistic,
- required this.threshold,
- required this.existingIssue,
- required this.bucket,
- });
-
- final BuilderStatistic statistic;
- final double threshold;
- final Issue existingIssue;
- final Bucket bucket;
-
- bool get isBelow => statistic.flakyRate < threshold;
-
- String get bucketString => bucket.toString().split('.').last;
-
- List<String> get issueLabels {
- final List<String> existingLabels = existingIssue.labels.map<String>((IssueLabel label) => label.name).toList();
- // Update the priority.
- if (!existingLabels.contains(kP0Label) && !isBelow) {
- existingLabels.add(kP0Label);
- existingLabels.remove(kP1Label);
- existingLabels.remove(kP2Label);
- existingLabels.remove(kP3Label);
- }
- return existingLabels;
- }
-
- String get issueUpdateComment {
- String result =
- '[$bucketString pool] flaky ratio for the past (up to) 100 commits between ${statistic.fromDate} and ${statistic.toDate} is ${_formatRate(statistic.flakyRate)}%. Flaky number: ${statistic.flakyNumber}; total number: ${statistic.totalNumber}.\n';
- if (statistic.flakyRate > 0.0) {
- result += '''
-One recent flaky example for a same commit: ${_issueBuildLink(builder: statistic.name, build: statistic.flakyBuildOfRecentCommit, bucket: bucket)}
-Commit: $_commitPrefix${statistic.recentCommit}
-Flaky builds:
-${_issueBuildLinks(builder: statistic.name, builds: statistic.flakyBuilds!, bucket: bucket)}
-
-Recent test runs:
-${_issueBuilderLink(statistic.name)}
-''';
- }
- return result;
- }
-}
-
-/// A builder to build the pull request title and body for marking test flaky
-class PullRequestBuilder {
- PullRequestBuilder({
- required this.statistic,
- required this.ownership,
- required this.issue,
- });
-
- final BuilderStatistic statistic;
- final TestOwnership ownership;
- final Issue issue;
-
- String get pullRequestTitle => 'Marks ${statistic.name} to be flaky';
- String get pullRequestBody => '${_buildHiddenMetaTags(name: statistic.name)}Issue link: ${issue.htmlUrl}\n';
- String? get pullRequestReviewer => ownership.owner;
-}
-
-/// A builder to build the pull request title and body for marking test unflaky
-class DeflakePullRequestBuilder {
- DeflakePullRequestBuilder({
- required this.name,
- required this.recordNumber,
- required this.ownership,
- this.issue,
- });
-
- final String? name;
- final Issue? issue;
- final TestOwnership ownership;
- final int recordNumber;
-
- String get pullRequestTitle => 'Marks $name to be unflaky';
- String get pullRequestBody {
- String body = _buildHiddenMetaTags(name: name);
- if (issue != null) {
- body +=
- 'The issue ${issue!.htmlUrl} has been closed, and the test has been passing for [$recordNumber consecutive runs](${Uri.encodeFull('$_flakeRecordPrefix"$name"')}).\n';
- } else {
- body +=
- 'The test has been passing for [$recordNumber consecutive runs](${Uri.encodeFull('$_flakeRecordPrefix"$name"')}).\n';
- }
- body += 'This test can be marked as unflaky.\n';
- return body;
- }
-
- String? get pullRequestReviewer => ownership.owner;
-}
-
-// TESTOWNER Regex
-
-const String kOwnerGroupName = 'owners';
-final RegExp devicelabTestOwners =
- RegExp('## Linux Android DeviceLab tests\n(?<$kOwnerGroupName>.+)## Host only framework tests', dotAll: true);
-final RegExp frameworkHostOnlyTestOwners =
- RegExp('## Host only framework tests\n(?<$kOwnerGroupName>.+)## Firebase tests', dotAll: true);
-final RegExp firebaselabTestOwners = RegExp('## Firebase tests\n(?<$kOwnerGroupName>.+)## Shards tests', dotAll: true);
-final RegExp shardTestOwners = RegExp('## Shards tests\n(?<$kOwnerGroupName>.+)', dotAll: true);
-
-// Utils methods
-
-/// Gets the existing flaky issues.
-///
-/// The state can be 'open', 'closed', or 'all'.
-Future<Map<String?, Issue>> getExistingIssues(GithubService gitHub, RepositorySlug slug, {String state = 'all'}) async {
- final Map<String?, Issue> nameToExistingIssue = <String?, Issue>{};
- for (final Issue issue in await gitHub.listIssues(slug, state: state, labels: <String>[kFlakeLabel])) {
- if (issue.htmlUrl.contains('pull') == true) {
- // For some reason, this github api may also return pull requests.
- continue;
- }
- final Map<String, dynamic>? metaTags = retrieveMetaTagsFromContent(issue.body);
- if (metaTags != null) {
- final String? name = metaTags['name'] as String?;
- if (!nameToExistingIssue.containsKey(name) || _isOtherIssueMoreImportant(nameToExistingIssue[name]!, issue)) {
- nameToExistingIssue[name] = issue;
- }
- }
- }
- return nameToExistingIssue;
-}
-
-/// Gets the existing open pull requests that make tests flaky.
-Future<Map<String?, PullRequest>> getExistingPRs(GithubService gitHub, RepositorySlug slug) async {
- final Map<String?, PullRequest> nameToExistingPRs = <String?, PullRequest>{};
- for (final PullRequest pr in await gitHub.listPullRequests(slug, null)) {
- try {
- if (pr.body == null) {
- continue;
- }
- final Map<String, dynamic>? metaTags = retrieveMetaTagsFromContent(pr.body!);
- if (metaTags != null) {
- nameToExistingPRs[metaTags['name'] as String] = pr;
- }
- } catch (e) {
- throw 'Unable to parse body of ${pr.htmlUrl}\n$e';
- }
- }
- return nameToExistingPRs;
-}
-
-/// File a GitHub flaky issue based on builder details in recent prod/staging runs.
-Future<Issue> fileFlakyIssue({
- required BuilderDetail builderDetail,
- required GithubService gitHub,
- required RepositorySlug slug,
- double threshold = kDefaultFlakyRatioThreshold,
- bool bringup = false,
-}) async {
- final IssueBuilder issueBuilder = IssueBuilder(
- statistic: builderDetail.statistic,
- ownership: builderDetail.ownership,
- threshold: kDefaultFlakyRatioThreshold,
- bringup: bringup,
- );
- return gitHub.createIssue(
- slug,
- title: issueBuilder.issueTitle,
- body: issueBuilder.issueBody,
- labels: issueBuilder.issueLabels,
- assignee: issueBuilder.issueAssignee,
- );
-}
-
-/// Looks up the owner of a builder in TESTOWNERS file.
-TestOwnership getTestOwnership(pb.Target target, BuilderType type, String testOwnersContent) {
- final TestOwner testOwner = TestOwner(type);
- return testOwner.getTestOwnership(target, testOwnersContent);
-}
-
-/// Gets the [BuilderType] of the builder by looking up the information in the
-/// ci.yaml.
-BuilderType getTypeForBuilder(String? targetName, CiYaml ciYaml, {bool unfilteredTargets = false}) {
- final List<String>? tags = _getTags(targetName, ciYaml, unfilteredTargets: unfilteredTargets);
- if (tags == null || tags.isEmpty) {
- return BuilderType.unknown;
- }
-
- bool hasFrameworkTag = false;
- bool hasHostOnlyTag = false;
- // If tags contain 'shard', it must be a shard test.
- // If tags contain 'devicelab', it must be a devicelab test.
- // If tags contain 'firebaselab`, it must be a firebase tests.
- // Otherwise, it is framework host only test if its tags contain both
- // 'framework' and 'hostonly'.
- for (String tag in tags) {
- if (tag == kCiYamlTargetTagsFirebaselab) {
- return BuilderType.firebaselab;
- } else if (tag == kCiYamlTargetTagsShard) {
- return BuilderType.shard;
- } else if (tag == kCiYamlTargetTagsDevicelab) {
- return BuilderType.devicelab;
- } else if (tag == kCiYamlTargetTagsFramework) {
- hasFrameworkTag = true;
- } else if (tag == kCiYamlTargetTagsHostonly) {
- hasHostOnlyTag = true;
- }
- }
- return hasFrameworkTag && hasHostOnlyTag ? BuilderType.frameworkHostOnly : BuilderType.unknown;
-}
-
-List<String>? _getTags(String? targetName, CiYaml ciYaml, {bool unfilteredTargets = false}) {
- final Set<Target> allUniqueTargets = {};
- if (!unfilteredTargets) {
- allUniqueTargets.addAll(ciYaml.presubmitTargets);
- allUniqueTargets.addAll(ciYaml.postsubmitTargets);
- } else {
- allUniqueTargets.addAll(ciYaml.targets);
- }
-
- final Target? target = allUniqueTargets.firstWhereOrNull((element) => element.value.name == targetName);
- return target?.tags;
-}
-
-bool _isOtherIssueMoreImportant(Issue original, Issue other) {
- // Open issues are always more important than closed issues. If both issue
- // are closed, the one that is most recently created is more important.
- if (original.isOpen && other.isOpen) {
- throw 'There should not be two open issues for the same test';
- } else if (original.isOpen && other.isClosed) {
- return false;
- } else if (original.isClosed && other.isOpen) {
- return true;
- } else {
- return other.createdAt!.isAfter(original.createdAt!);
- }
-}
-
-String _buildHiddenMetaTags({String? name}) {
- return '''<!-- meta-tags: To be used by the automation script only, DO NOT MODIFY.
-{
- "name": "$name"
-}
--->
-''';
-}
-
-final RegExp _issueHiddenMetaTagsRegex =
- RegExp(r'<!-- meta-tags: To be used by the automation script only, DO NOT MODIFY\.(?<meta>.+)-->', dotAll: true);
-
-/// Checks whether the github content contains meta tags and returns the meta
-/// tags if it does.
-///
-/// The script generated contents for issue bodies or pull request bodies
-/// contain the meta tags. Using this method is a reliable way to check whether
-/// a issue or pull request is generated by this script.
-Map<String, dynamic>? retrieveMetaTagsFromContent(String content) {
- final RegExpMatch? match = _issueHiddenMetaTagsRegex.firstMatch(content);
- if (match == null) {
- return null;
- }
- return jsonDecode(match.namedGroup('meta')!) as Map<String, dynamic>?;
-}
-
-String _formatRate(double rate) => (rate * 100).toStringAsFixed(2);
-
-String _issueBuildLinks({String? builder, required List<String> builds, Bucket bucket = Bucket.prod}) {
- return builds.map((String build) => _issueBuildLink(builder: builder, build: build, bucket: bucket)).join('\n');
-}
-
-String _issueSummary(BuilderStatistic statistic, double threshold, bool bringup) {
- final String summary;
- if (bringup) {
- summary =
- 'The post-submit test builder `${statistic.name}`, which has been marked `bringup: true`, had ${statistic.flakyNumber} flakes over past ${statistic.totalNumber} commits.';
- } else {
- summary =
- 'The post-submit test builder `${statistic.name}` had a flaky ratio ${_formatRate(statistic.flakyRate)}% for the past (up to) 100 commits, which is above our ${_formatRate(threshold)}% threshold.';
- }
- return summary;
-}
-
-String _issueBuildLink({String? builder, String? build, Bucket bucket = Bucket.prod}) {
- final String buildPrefix = bucket == Bucket.staging ? _stagingBuildPrefix : _prodBuildPrefix;
- return Uri.encodeFull('$buildPrefix$builder/$build');
-}
-
-String _issueBuilderLink(String? builder) {
- return Uri.encodeFull('$_buildDashboardPrefix?taskFilter=$builder');
-}
-
-String? getTeamLabelFromTeam(Team? team) {
- return switch (team) {
- Team.framework => kFrameworkLabel,
- Team.engine => kEngineLabel,
- Team.tool => kToolLabel,
- Team.web => kWebLabel,
- Team.infra => kInfraLabel,
- Team.android => kAndroidLabel,
- Team.ios => kIosLabel,
- Team.release => kReleaseLabel,
- Team.plugins => kEcosystemLabel,
- Team.unknown || null => null,
- };
-}
-
-enum BuilderType {
- devicelab,
- frameworkHostOnly,
- shard,
- firebaselab,
- unknown,
-}
-
-enum Bucket {
- prod,
- staging,
-}
-
-enum Team {
- framework,
- engine,
- tool,
- web,
- infra,
- android,
- ios,
- release,
- plugins,
- unknown,
-}
-
-class TestOwnership {
- TestOwnership(
- this.owner,
- this.team,
- );
- String? owner;
- Team? team;
-}
-
-class BuilderDetail {
- const BuilderDetail({
- required this.statistic,
- required this.existingIssue,
- required this.existingPullRequest,
- required this.isMarkedFlaky,
- required this.ownership,
- required this.type,
- });
- final BuilderStatistic statistic;
- final Issue? existingIssue;
- final PullRequest? existingPullRequest;
- final TestOwnership ownership;
- final bool isMarkedFlaky;
- final BuilderType type;
-}
diff --git a/app_dart/lib/src/request_handlers/flush_cache.dart b/app_dart/lib/src/request_handlers/flush_cache.dart
deleted file mode 100644
index 824a0c7..0000000
--- a/app_dart/lib/src/request_handlers/flush_cache.dart
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-
-import 'package:meta/meta.dart';
-
-import '../request_handling/api_request_handler.dart';
-import '../request_handling/body.dart';
-import '../request_handling/exceptions.dart';
-import '../service/cache_service.dart';
-import '../service/config.dart';
-
-/// Trigger a cache flush on a config key and return empty response if successful.
-///
-/// If [cacheKeyParam] is not passed, throws [BadRequestException].
-/// If the cache does not have the given key, throws [NotFoundException].
-@immutable
-class FlushCache extends ApiRequestHandler<Body> {
- const FlushCache({
- required super.config,
- required super.authenticationProvider,
- required this.cache,
- });
-
- final CacheService cache;
-
- /// Name of the query parameter passed to the endpoint.
- ///
- /// The value is expected to be an existing value from [CocoonConfig].
- static const String cacheKeyParam = 'key';
-
- @override
- Future<Body> get() async {
- checkRequiredQueryParameters(<String>[cacheKeyParam]);
- final String cacheKey = request!.uri.queryParameters[cacheKeyParam]!;
-
- // To validate cache flushes, validate that the key exists.
- await cache.getOrCreate(
- Config.configCacheName,
- cacheKey,
- createFn: () => throw NotFoundException('Failed to find cache key: $cacheKey'),
- );
-
- await cache.purge(Config.configCacheName, cacheKey);
-
- return Body.empty;
- }
-}
diff --git a/app_dart/lib/src/request_handlers/get_build_status.dart b/app_dart/lib/src/request_handlers/get_build_status.dart
deleted file mode 100644
index e434842..0000000
--- a/app_dart/lib/src/request_handlers/get_build_status.dart
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-
-import 'package:github/github.dart';
-import 'package:meta/meta.dart';
-
-import '../../protos.dart' show BuildStatusResponse, EnumBuildStatus;
-import '../request_handling/body.dart';
-import '../request_handling/request_handler.dart';
-import '../service/build_status_provider.dart';
-import '../service/datastore.dart';
-
-@immutable
-class GetBuildStatus extends RequestHandler<Body> {
- const GetBuildStatus({
- required super.config,
- @visibleForTesting DatastoreServiceProvider? datastoreProvider,
- @visibleForTesting BuildStatusServiceProvider? buildStatusProvider,
- }) : datastoreProvider = datastoreProvider ?? DatastoreService.defaultProvider,
- buildStatusProvider = buildStatusProvider ?? BuildStatusService.defaultProvider;
- final DatastoreServiceProvider datastoreProvider;
- final BuildStatusServiceProvider buildStatusProvider;
-
- static const String kRepoParam = 'repo';
-
- @override
- Future<Body> get() async {
- final DatastoreService datastore = datastoreProvider(config.db);
- final BuildStatusService buildStatusService = buildStatusProvider(datastore);
- final String repoName = request!.uri.queryParameters[kRepoParam] ?? 'flutter';
- final RepositorySlug slug = RepositorySlug('flutter', repoName);
- final BuildStatus status = (await buildStatusService.calculateCumulativeStatus(slug))!;
- final BuildStatusResponse response = BuildStatusResponse()
- ..buildStatus = status.succeeded ? EnumBuildStatus.success : EnumBuildStatus.failure
- ..failingTasks.addAll(status.failedTasks);
-
- return Body.forJson(response.writeToJsonMap());
- }
-}
diff --git a/app_dart/lib/src/request_handlers/get_green_commits.dart b/app_dart/lib/src/request_handlers/get_green_commits.dart
deleted file mode 100644
index 8a3adba..0000000
--- a/app_dart/lib/src/request_handlers/get_green_commits.dart
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2021 The Flutter 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:async';
-
-import 'package:cocoon_service/src/model/appengine/commit.dart';
-import 'package:cocoon_service/src/model/appengine/stage.dart';
-import 'package:cocoon_service/src/model/appengine/task.dart';
-import 'package:github/github.dart';
-import 'package:meta/meta.dart';
-
-import '../request_handling/body.dart';
-import '../request_handling/request_handler.dart';
-import '../service/build_status_provider.dart';
-import '../service/config.dart';
-import '../service/datastore.dart';
-
-/// Returns [List<String>] of the commit shas that had all passing tests.
-///
-/// A [CommitStatus] that have all passing tests is used to help the release tooling find commits Flutter infrastructure has validated.
-/// In order to qualify as a [CommitStatus] that have all passing tests, the rules are:
-/// 1. The [Commit] inside [CommitStatus] had all its tests run (at least those that are not in bringup)
-/// 2. all the blocking [Task] in [CommitStatus] should pass
-/// A [List<String>] of commit shas of the qualified [CommitStatus]s are returned, in the order of [Commit] timestamp, i.e.,
-/// A [Commit] with an earlier timestamp will apprear earlier in the result [List<String>], as compared to another [Commit]
-/// with a later timestamp.
-///
-/// Parameters:
-/// branch: defaults to the defaults branch for the repository.
-/// repo: default: 'flutter'. Name of the repository.
-///
-/// GET: /api/public/get-green-commits?repo=$repo
-
-@immutable
-class GetGreenCommits extends RequestHandler<Body> {
- const GetGreenCommits({
- required super.config,
- @visibleForTesting this.datastoreProvider = DatastoreService.defaultProvider,
- @visibleForTesting BuildStatusServiceProvider? buildStatusProvider,
- }) : buildStatusProvider = buildStatusProvider ?? BuildStatusService.defaultProvider;
-
- final DatastoreServiceProvider datastoreProvider;
- final BuildStatusServiceProvider buildStatusProvider;
-
- static const String kBranchParam = 'branch';
- static const String kRepoParam = 'repo';
-
- @override
- Future<Body> get() async {
- final String repoName = request!.uri.queryParameters[kRepoParam] ?? Config.flutterSlug.name;
- final RepositorySlug slug = RepositorySlug('flutter', repoName);
- final String branch = request!.uri.queryParameters[kBranchParam] ?? Config.defaultBranch(slug);
- final DatastoreService datastore = datastoreProvider(config.db);
- final BuildStatusService buildStatusService = buildStatusProvider(datastore);
- final int commitNumber = config.commitNumber;
-
- final List<String?> greenCommits = await buildStatusService
- .retrieveCommitStatus(
- limit: commitNumber,
- branch: branch,
- slug: slug,
- )
- .where((CommitStatus status) => everyNonFlakyTaskSucceed(status))
- .map<String?>((CommitStatus status) => status.commit.sha)
- .toList();
-
- return Body.forJson(greenCommits);
- }
-
- bool everyNonFlakyTaskSucceed(CommitStatus status) {
- return status.stages.every(
- (Stage stage) => stage.tasks
- .where((Task task) => !task.isFlaky!)
- .every((Task nonFlakyTask) => nonFlakyTask.status == Task.statusSucceeded),
- );
- }
-}
diff --git a/app_dart/lib/src/request_handlers/get_release_branches.dart b/app_dart/lib/src/request_handlers/get_release_branches.dart
deleted file mode 100644
index f698e2f..0000000
--- a/app_dart/lib/src/request_handlers/get_release_branches.dart
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2020 The Flutter 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:async';
-
-import 'package:cocoon_service/src/request_handling/request_handler.dart';
-import 'package:github/github.dart';
-
-import '../request_handling/body.dart';
-import '../service/branch_service.dart';
-import '../service/config.dart';
-import '../service/github_service.dart';
-
-/// Return a list of release branch information.
-///
-/// GET: /api/public/get-release-branches
-///
-/// Response: Status 200 OK
-///[
-/// {
-/// "branch":"flutter-2.13-candidate.0",
-/// "name":"stable"
-/// },
-/// {
-/// "branch":"flutter-3.2-candidate.5",
-/// "name":"beta"
-/// },
-/// {
-/// "branch":"flutter-3.4-candidate.5",
-/// "name":"dev"
-/// }
-///]
-
-class GetReleaseBranches extends RequestHandler<Body> {
- GetReleaseBranches({required super.config, required this.branchService});
-
- final BranchService branchService;
-
- @override
- Future<Body> get() async {
- final GitHub github = await config.createGitHubClient(slug: Config.flutterSlug);
- final GithubService githubService = GithubService(github);
- final List<Map<String, String>> branchNames =
- await branchService.getReleaseBranches(githubService: githubService, slug: Config.flutterSlug);
- return Body.forJson(branchNames);
- }
-}
diff --git a/app_dart/lib/src/request_handlers/get_repos.dart b/app_dart/lib/src/request_handlers/get_repos.dart
deleted file mode 100644
index 0366ab1..0000000
--- a/app_dart/lib/src/request_handlers/get_repos.dart
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2020 The Flutter 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:async';
-
-import 'package:github/github.dart';
-import 'package:meta/meta.dart';
-
-import '../request_handling/body.dart';
-import '../request_handling/request_handler.dart';
-import '../service/config.dart';
-
-/// Returns [Config.supportedRepos] as a list of repo names.
-@immutable
-class GetRepos extends RequestHandler<Body> {
- const GetRepos({
- required super.config,
- });
-
- @override
- Future<Body> get() async {
- final List<String> repos = config.supportedRepos.map((RepositorySlug slug) => slug.name).toList();
- return Body.forJson(repos);
- }
-}
diff --git a/app_dart/lib/src/request_handlers/get_status.dart b/app_dart/lib/src/request_handlers/get_status.dart
deleted file mode 100644
index 55f812f..0000000
--- a/app_dart/lib/src/request_handlers/get_status.dart
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-
-import 'package:gcloud/db.dart';
-import 'package:github/github.dart';
-import 'package:meta/meta.dart';
-
-import '../model/appengine/commit.dart';
-import '../model/appengine/key_helper.dart';
-import '../request_handling/body.dart';
-import '../request_handling/exceptions.dart';
-import '../request_handling/request_handler.dart';
-import '../service/build_status_provider.dart';
-import '../service/config.dart';
-import '../service/datastore.dart';
-
-@immutable
-class GetStatus extends RequestHandler<Body> {
- const GetStatus({
- required super.config,
- @visibleForTesting this.datastoreProvider = DatastoreService.defaultProvider,
- @visibleForTesting this.buildStatusProvider = BuildStatusService.defaultProvider,
- });
-
- final DatastoreServiceProvider datastoreProvider;
- final BuildStatusServiceProvider buildStatusProvider;
-
- static const String kLastCommitKeyParam = 'lastCommitKey';
- static const String kBranchParam = 'branch';
- static const String kRepoParam = 'repo';
-
- @override
- Future<Body> get() async {
- final String? encodedLastCommitKey = request!.uri.queryParameters[kLastCommitKeyParam];
- final String repoName = request!.uri.queryParameters[kRepoParam] ?? Config.flutterSlug.name;
- final RepositorySlug slug = RepositorySlug('flutter', repoName);
- final String branch = request!.uri.queryParameters[kBranchParam] ?? Config.defaultBranch(slug);
- final DatastoreService datastore = datastoreProvider(config.db);
- final BuildStatusService buildStatusService = buildStatusProvider(datastore);
- final KeyHelper keyHelper = config.keyHelper;
- final int commitNumber = config.commitNumber;
- final int lastCommitTimestamp = await _obtainTimestamp(encodedLastCommitKey, keyHelper, datastore);
-
- final List<SerializableCommitStatus> statuses = await buildStatusService
- .retrieveCommitStatus(
- limit: commitNumber,
- timestamp: lastCommitTimestamp,
- branch: branch,
- slug: slug,
- )
- .map<SerializableCommitStatus>(
- (CommitStatus status) => SerializableCommitStatus(status, keyHelper.encode(status.commit.key)),
- )
- .toList();
-
- return Body.forJson(<String, dynamic>{
- 'Statuses': statuses,
- });
- }
-
- Future<int> _obtainTimestamp(String? encodedLastCommitKey, KeyHelper keyHelper, DatastoreService datastore) async {
- /// [lastCommitTimestamp] is initially set as the current time, which allows query return
- /// latest commit list. If [owerKeyParam] is not empty, [lastCommitTimestamp] will be set
- /// as the timestamp of that [commit], and the query will return lastest commit
- /// list whose timestamp is smaller than [lastCommitTimestamp].
- int lastCommitTimestamp = DateTime.now().millisecondsSinceEpoch;
-
- if (encodedLastCommitKey != null) {
- final Key<String> ownerKey = keyHelper.decode(encodedLastCommitKey) as Key<String>;
- final Commit commit = await datastore.db.lookupValue<Commit>(
- ownerKey,
- orElse: () => throw NotFoundException('Failed to find commit with key $ownerKey'),
- );
-
- lastCommitTimestamp = commit.timestamp!;
- }
- return lastCommitTimestamp;
- }
-}
-
-/// The serialized representation of a [CommitStatus].
-// TODO(tvolkert): Directly serialize [CommitStatus] once frontends migrate to new format.
-class SerializableCommitStatus {
- const SerializableCommitStatus(this.status, this.key);
-
- final CommitStatus status;
- final String key;
-
- Map<String, dynamic> toJson() {
- return <String, dynamic>{
- 'Checklist': <String, dynamic>{
- 'Key': key,
- 'Checklist': SerializableCommit(status.commit).facade,
- },
- 'Stages': status.stages,
- };
- }
-}
diff --git a/app_dart/lib/src/request_handlers/github/webhook_subscription.dart b/app_dart/lib/src/request_handlers/github/webhook_subscription.dart
deleted file mode 100644
index 05f7762..0000000
--- a/app_dart/lib/src/request_handlers/github/webhook_subscription.dart
+++ /dev/null
@@ -1,638 +0,0 @@
-// Copyright 2019 The Flutter 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:convert';
-
-import 'package:cocoon_service/src/service/commit_service.dart';
-import 'package:github/github.dart';
-import 'package:github/hooks.dart';
-import 'package:meta/meta.dart';
-
-import '../../../protos.dart' as pb;
-import '../../model/gerrit/commit.dart';
-import '../../model/github/checks.dart' as cocoon_checks;
-import '../../request_handling/body.dart';
-import '../../request_handling/exceptions.dart';
-import '../../request_handling/subscription_handler.dart';
-import '../../service/config.dart';
-import '../../service/datastore.dart';
-import '../../service/gerrit_service.dart';
-import '../../service/github_checks_service.dart';
-import '../../service/logging.dart';
-import '../../service/scheduler.dart';
-
-// Filenames which are not actually tests.
-const List<String> kNotActuallyATest = <String>[
- 'packages/flutter/lib/src/gestures/hit_test.dart',
-];
-
-/// List of repos that require check for tests.
-Set<RepositorySlug> kNeedsTests = <RepositorySlug>{
- Config.engineSlug,
- Config.flutterSlug,
- Config.packagesSlug,
-};
-
-final RegExp kEngineTestRegExp = RegExp(r'(tests?|benchmarks?)\.(dart|java|mm|m|cc|sh|py)$');
-
-// Extentions for files that use // for single line comments.
-// See [_allChangesAreCodeComments] for more.
-@visibleForTesting
-const Set<String> knownCommentCodeExtensions = <String>{
- 'cc',
- 'cpp',
- 'dart',
- 'gradle',
- 'groovy',
- 'java',
- 'kt',
- 'm',
- 'mm',
- 'swift',
-};
-
-/// Subscription for processing GitHub webhooks.
-///
-/// The PubSub subscription is set up here:
-/// https://console.cloud.google.com/cloudpubsub/subscription/detail/github-webhooks-sub?project=flutter-dashboard&tab=overview
-///
-/// This endpoint enables Cocoon to recover from outages.
-///
-/// This endpoint takes in a POST request with the GitHub event JSON.
-// TODO(chillers): There's potential now to split this into seprate subscriptions
-// for various activities (such as infra vs releases). This would mitigate
-// breakages across Cocoon.
-@immutable
-class GithubWebhookSubscription extends SubscriptionHandler {
- /// Creates a subscription for processing GitHub webhooks.
- const GithubWebhookSubscription({
- required super.cache,
- required super.config,
- required this.scheduler,
- required this.gerritService,
- required this.commitService,
- this.githubChecksService,
- this.datastoreProvider = DatastoreService.defaultProvider,
- super.authProvider,
- }) : super(subscriptionName: 'github-webhooks-sub');
-
- /// Cocoon scheduler to trigger tasks against changes from GitHub.
- final Scheduler scheduler;
-
- /// To verify whether a commit was mirrored to GoB.
- final GerritService gerritService;
-
- /// Used to handle push events and create commits based on those events.
- final CommitService commitService;
-
- /// To provide build status updates to GitHub pull requests.
- final GithubChecksService? githubChecksService;
-
- final DatastoreServiceProvider datastoreProvider;
-
- @override
- Future<Body> post() async {
- if (message.data == null || message.data!.isEmpty) {
- log.warning('GitHub webhook message was empty. No-oping');
- return Body.empty;
- }
-
- final pb.GithubWebhookMessage webhook = pb.GithubWebhookMessage.fromJson(message.data!);
-
- log.fine('Processing ${webhook.event}');
- log.finest(webhook.payload);
- switch (webhook.event) {
- case 'pull_request':
- await _handlePullRequest(webhook.payload);
- break;
- case 'check_run':
- final Map<String, dynamic> event = jsonDecode(webhook.payload) as Map<String, dynamic>;
- final cocoon_checks.CheckRunEvent checkRunEvent = cocoon_checks.CheckRunEvent.fromJson(event);
- if (await scheduler.processCheckRun(checkRunEvent) == false) {
- throw InternalServerError('Failed to process check_run event. checkRunEvent: $checkRunEvent');
- }
- break;
- case 'push':
- final Map<String, dynamic> event = jsonDecode(webhook.payload) as Map<String, dynamic>;
- final String branch = event['ref'].split('/')[2]; // Eg: refs/heads/beta would return beta.
- final String repository = event['repository']['name'];
- // If the branch is beta/stable, then a commit wasn't created through a PR,
- // meaning the commit needs to be added to the datastore here instead.
- if (repository == 'flutter' && (branch == 'stable' || branch == 'beta')) {
- await commitService.handlePushGithubRequest(event);
- }
- break;
- case 'create':
- final CreateEvent createEvent = CreateEvent.fromJson(json.decode(webhook.payload) as Map<String, dynamic>);
- final RegExp candidateBranchRegex = RegExp(r'flutter-\d+\.\d+-candidate\.\d+');
- // Create a commit object for candidate branches in the datastore so
- // dart-internal builds that are triggered by the initial branch
- // creation have an associated commit.
- if (candidateBranchRegex.hasMatch(createEvent.ref!)) {
- log.fine('Branch ${createEvent.ref} is a candidate branch, creating new commit in the datastore');
- await commitService.handleCreateGithubRequest(createEvent);
- }
- }
-
- return Body.empty;
- }
-
- /// Handles a GitHub webhook with the event type "pull_request".
- ///
- /// Regarding merged pull request events: the commit must be mirrored to GoB
- /// before we can trigger postsubmit tasks. If the commit is not found, the
- /// event will be failed so it can be retried. As of Jan 26, 2023, the
- /// retention policy for Pub/Sub messages is 7 days. This event will be
- /// retried with exponential backoff within that time period. The GoB mirror
- /// should be caught up within that time frame via either the internal
- /// mirroring service or [VacuumGithubCommits].
- Future<void> _handlePullRequest(
- String rawRequest,
- ) async {
- final PullRequestEvent? pullRequestEvent = await _getPullRequestEvent(rawRequest);
- if (pullRequestEvent == null) {
- throw const BadRequestException('Expected pull request event.');
- }
- final String? eventAction = pullRequestEvent.action;
- final PullRequest pr = pullRequestEvent.pullRequest!;
-
- // See the API reference:
- // https://developer.github.com/v3/activity/events/types/#pullrequestevent
- // which unfortunately is a bit light on explanations.
- log.fine('Processing $eventAction for ${pr.htmlUrl}');
- switch (eventAction) {
- case 'closed':
- // If it was closed without merging, cancel any outstanding tryjobs.
- // We'll leave unfinished jobs if it was merged since we care about those
- // results.
- await scheduler.cancelPreSubmitTargets(
- pullRequest: pr,
- reason: (!pr.merged!) ? 'Pull request closed' : 'Pull request merged',
- );
-
- if (pr.merged!) {
- log.fine('Pull request ${pr.number} was closed and merged.');
- if (await _commitExistsInGob(pr)) {
- log.fine('Merged commit was found on GoB mirror. Scheduling postsubmit tasks...');
- return scheduler.addPullRequest(pr);
- }
- throw InternalServerError(
- '${pr.mergeCommitSha!} was not found on GoB. Failing so this event can be retried...',
- );
- }
- break;
- case 'edited':
- // Editing a PR should not trigger new jobs, but may update whether
- // it has tests.
- await _checkForTests(pullRequestEvent);
- break;
- case 'opened':
- case 'reopened':
- // These cases should trigger LUCI jobs. The closed event should happen
- // before these which should cancel all in progress checks.
- await _checkForTests(pullRequestEvent);
- await _scheduleIfMergeable(pullRequestEvent);
- await _tryReleaseApproval(pullRequestEvent);
- break;
- case 'labeled':
- break;
- case 'synchronize':
- // This indicates the PR has new commits. We need to cancel old jobs
- // and schedule new ones.
- await _scheduleIfMergeable(pullRequestEvent);
- break;
- // Ignore the rest of the events.
- case 'ready_for_review':
- case 'unlabeled':
- case 'assigned':
- case 'locked':
- case 'review_request_removed':
- case 'review_requested':
- case 'unassigned':
- case 'unlocked':
- break;
- }
- }
-
- Future<bool> _commitExistsInGob(PullRequest pr) async {
- final RepositorySlug slug = pr.base!.repo!.slug();
- final String sha = pr.mergeCommitSha!;
- final GerritCommit? gobCommit = await gerritService.findMirroredCommit(slug, sha);
- return gobCommit != null;
- }
-
- /// This method assumes that jobs should be cancelled if they are already
- /// runnning.
- Future<void> _scheduleIfMergeable(
- PullRequestEvent pullRequestEvent,
- ) async {
- final PullRequest pr = pullRequestEvent.pullRequest!;
- final RepositorySlug slug = pullRequestEvent.repository!.slug();
-
- log.info(
- 'Scheduling tasks if mergeable(${pr.mergeable}): owner=${slug.owner} repo=${slug.name} and pr=${pr.number}',
- );
-
- // The mergeable flag may be null. False indicates there's a merge conflict,
- // null indicates unknown. Err on the side of allowing the job to run.
- if (pr.mergeable == false) {
- final RepositorySlug slug = pullRequestEvent.repository!.slug();
- final GitHub gitHubClient = await config.createGitHubClient(pullRequest: pr);
- final String body = config.mergeConflictPullRequestMessage;
- if (!await _alreadyCommented(gitHubClient, pr, body)) {
- await gitHubClient.issues.createComment(slug, pr.number!, body);
- }
- return;
- }
-
- await scheduler.triggerPresubmitTargets(pullRequest: pr);
- }
-
- /// Release tooling generates cherrypick pull requests that should be granted an approval.
- Future<void> _tryReleaseApproval(
- PullRequestEvent pullRequestEvent,
- ) async {
- final PullRequest pr = pullRequestEvent.pullRequest!;
- final RepositorySlug slug = pullRequestEvent.repository!.slug();
-
- final String defaultBranch = Config.defaultBranch(slug);
- final String? branch = pr.base?.ref;
- if (branch == null || branch.contains(defaultBranch)) {
- // This isn't a release branch PR
- return;
- }
-
- final List<String> releaseAccounts = await config.releaseAccounts;
- if (releaseAccounts.contains(pr.user?.login) == false) {
- // The author isn't in the list of release accounts, do nothing
- return;
- }
-
- final GitHub gitHubClient = config.createGitHubClientWithToken(await config.githubOAuthToken);
- final CreatePullRequestReview review = CreatePullRequestReview(slug.owner, slug.name, pr.number!, 'APPROVE');
- await gitHubClient.pullRequests.createReview(slug, review);
- }
-
- Future<void> _checkForTests(PullRequestEvent pullRequestEvent) async {
- final PullRequest pr = pullRequestEvent.pullRequest!;
- final String? eventAction = pullRequestEvent.action;
- final RepositorySlug slug = pr.base!.repo!.slug();
- final bool isTipOfTree = pr.base!.ref == Config.defaultBranch(slug);
- final GitHub gitHubClient = await config.createGitHubClient(pullRequest: pr);
- await _validateRefs(gitHubClient, pr);
- if (kNeedsTests.contains(slug) && isTipOfTree) {
- switch (slug.name) {
- case 'flutter':
- return _applyFrameworkRepoLabels(gitHubClient, eventAction, pr);
- case 'engine':
- return _applyEngineRepoLabels(gitHubClient, eventAction, pr);
- case 'packages':
- return _applyPackageTestChecks(gitHubClient, eventAction, pr);
- }
- }
- }
-
- Future<void> _applyFrameworkRepoLabels(GitHub gitHubClient, String? eventAction, PullRequest pr) async {
- if (pr.user!.login == 'engine-flutter-autoroll') {
- return;
- }
-
- final RepositorySlug slug = pr.base!.repo!.slug();
- log.info('Applying framework repo labels for: owner=${slug.owner} repo=${slug.name} and pr=${pr.number}');
- final Stream<PullRequestFile> files = gitHubClient.pullRequests.listFiles(slug, pr.number!);
-
- final Set<String> labels = <String>{};
- bool hasTests = false;
- bool needsTests = false;
-
- await for (PullRequestFile file in files) {
- final String filename = file.filename!;
-
- if (_fileContainsAddedCode(file) &&
- !_isTestExempt(filename) &&
- !filename.startsWith('dev/bots/') &&
- !filename.endsWith('.gitignore')) {
- needsTests = !_allChangesAreCodeComments(file);
- }
-
- // Check to see if tests were submitted with this PR.
- if (_isATest(filename)) {
- hasTests = true;
- }
- }
-
- if (pr.user!.login == 'fluttergithubbot') {
- needsTests = false;
- labels.addAll(<String>['c: tech-debt', 'c: flake']);
- }
-
- if (labels.isNotEmpty) {
- await gitHubClient.issues.addLabelsToIssue(slug, pr.number!, labels.toList());
- }
-
- // We do not need to add test labels if this is an auto roller author.
- if (config.rollerAccounts.contains(pr.user!.login)) {
- return;
- }
-
- if (!hasTests && needsTests && !pr.draft! && !_isReleaseBranch(pr)) {
- final String body = config.missingTestsPullRequestMessage;
- if (!await _alreadyCommented(gitHubClient, pr, body)) {
- await gitHubClient.issues.createComment(slug, pr.number!, body);
- }
- }
- }
-
- bool _isATest(String filename) {
- if (kNotActuallyATest.any(filename.endsWith)) {
- return false;
- }
- // Check for Objective-C tests which end in either "Tests.m" or "Test.m"
- // in the "dev" directory.
- final RegExp objectiveCTestRegex = RegExp(r'.*dev\/.*Test[s]?\.m$');
- return filename.endsWith('_test.dart') ||
- filename.endsWith('.expect') ||
- filename.contains('test_fixes') ||
- // Include updates to test utilities or test data
- filename.contains('packages/flutter_tools/test/') ||
- filename.startsWith('dev/bots/analyze.dart') ||
- filename.startsWith('dev/bots/test.dart') ||
- filename.startsWith('dev/devicelab/bin/tasks') ||
- filename.startsWith('dev/devicelab/lib/tasks') ||
- filename.startsWith('dev/benchmarks') ||
- objectiveCTestRegex.hasMatch(filename);
- }
-
- /// Returns true if changes to [filename] are exempt from the testing
- /// requirement, across repositories.
- bool _isTestExempt(String filename) {
- return filename.contains('.ci.yaml') ||
- filename.contains('analysis_options.yaml') ||
- filename.contains('AUTHORS') ||
- filename.contains('CODEOWNERS') ||
- filename.contains('TESTOWNERS') ||
- filename.contains('pubspec.yaml') ||
- // Exempt categories.
- filename.contains('.github/') ||
- filename.endsWith('.md') ||
- // Exempt paths.
- filename.startsWith('dev/devicelab/lib/versions/gallery.dart') ||
- filename.startsWith('dev/integration_tests') ||
- filename.startsWith('impeller/fixtures') ||
- filename.startsWith('impeller/golden_tests') ||
- filename.startsWith('impeller/playground') ||
- filename.startsWith('shell/platform/embedder/tests') ||
- filename.startsWith('shell/platform/embedder/fixtures');
- }
-
- Future<void> _applyEngineRepoLabels(GitHub gitHubClient, String? eventAction, PullRequest pr) async {
- // Do not apply the test labels for the autoroller accounts.
- if (pr.user!.login == 'skia-flutter-autoroll') {
- return;
- }
-
- final RepositorySlug slug = pr.base!.repo!.slug();
- final Stream<PullRequestFile> files = gitHubClient.pullRequests.listFiles(slug, pr.number!);
- bool hasTests = false;
- bool needsTests = false;
-
- await for (PullRequestFile file in files) {
- final String filename = file.filename!.toLowerCase();
- if (_fileContainsAddedCode(file) && filename.endsWith('.dart') ||
- filename.endsWith('.mm') ||
- filename.endsWith('.m') ||
- filename.endsWith('.java') ||
- filename.endsWith('.cc')) {
- needsTests = !_allChangesAreCodeComments(file);
- }
-
- if (kEngineTestRegExp.hasMatch(filename)) {
- hasTests = true;
- }
- }
-
- // We do not need to add test labels if this is an auto roller author.
- if (config.rollerAccounts.contains(pr.user!.login)) {
- return;
- }
-
- if (!hasTests && needsTests && !pr.draft! && !_isReleaseBranch(pr)) {
- final String body = config.missingTestsPullRequestMessage;
- if (!await _alreadyCommented(gitHubClient, pr, body)) {
- await gitHubClient.issues.createComment(slug, pr.number!, body);
- }
- }
- }
-
- bool _fileContainsAddedCode(PullRequestFile file) {
- // When null, do not assume 0 lines have been added.
- final int linesAdded = file.additionsCount ?? 1;
- final int linesDeleted = file.deletionsCount ?? 0;
- final int linesTotal = file.changesCount ?? linesDeleted + linesAdded;
- return linesAdded > 0 || linesDeleted != linesTotal;
- }
-
- // Runs automated test checks for both flutter/packages.
- Future<void> _applyPackageTestChecks(GitHub gitHubClient, String? eventAction, PullRequest pr) async {
- final RepositorySlug slug = pr.base!.repo!.slug();
- final Stream<PullRequestFile> files = gitHubClient.pullRequests.listFiles(slug, pr.number!);
- bool hasTests = false;
- bool needsTests = false;
-
- await for (PullRequestFile file in files) {
- final String filename = file.filename!;
-
- if (_fileContainsAddedCode(file) &&
- !_isTestExempt(filename) &&
- !filename.contains('.ci/') &&
- // Custom package-specific test runners. These do not count as tests
- // for the purposes of testing a change that otherwise needs tests,
- // but since they are the driver for tests they don't need test
- // coverage.
- !filename.endsWith('tool/run_tests.dart') &&
- !filename.endsWith('run_tests.sh')) {
- needsTests = !_allChangesAreCodeComments(file);
- }
- // See https://github.com/flutter/flutter/wiki/Plugin-Tests for discussion
- // of various plugin test types and locations.
- if (filename.endsWith('_test.dart') ||
- // Native iOS/macOS tests.
- filename.contains('RunnerTests/') ||
- filename.contains('RunnerUITests/') ||
- // Native Android tests.
- filename.contains('android/src/test/') ||
- filename.contains('androidTest/') ||
- // Native Linux tests.
- filename.endsWith('_test.cc') ||
- // Native Windows tests.
- filename.endsWith('_test.cpp') ||
- // Pigeon native tests.
- filename.contains('/platform_tests/') ||
- // Test files in package-specific test folders.
- filename.contains('go_router/test_fixes/') ||
- filename.contains('go_router_builder/test_inputs/')) {
- hasTests = true;
- }
- }
-
- // We do not need to add test labels if this is an auto roller author.
- if (config.rollerAccounts.contains(pr.user!.login)) {
- return;
- }
-
- if (!hasTests && needsTests && !pr.draft! && !_isReleaseBranch(pr)) {
- final String body = config.missingTestsPullRequestMessage;
- if (!await _alreadyCommented(gitHubClient, pr, body)) {
- await gitHubClient.issues.createComment(slug, pr.number!, body);
- }
- }
- }
-
- /// Validate the base and head refs of the PR.
- Future<void> _validateRefs(
- GitHub gitHubClient,
- PullRequest pr,
- ) async {
- final RepositorySlug slug = pr.base!.repo!.slug();
- String body;
- const List<String> releaseChannels = <String>[
- 'stable',
- 'beta',
- 'dev',
- ];
- // Close PRs that use a release branch as a source.
- if (releaseChannels.contains(pr.head!.ref)) {
- body = config.wrongHeadBranchPullRequestMessage(pr.head!.ref!);
- if (!await _alreadyCommented(gitHubClient, pr, body)) {
- await gitHubClient.pullRequests.edit(
- slug,
- pr.number!,
- state: 'closed',
- );
- await gitHubClient.issues.createComment(slug, pr.number!, body);
- }
- return;
- }
- final String defaultBranchName = Config.defaultBranch(pr.base!.repo!.slug());
- final String baseName = pr.base!.ref!;
- if (baseName == defaultBranchName) {
- return;
- }
- if (_isReleaseBranch(pr)) {
- body = config.releaseBranchPullRequestMessage;
- if (!await _alreadyCommented(gitHubClient, pr, body)) {
- await gitHubClient.issues.createComment(slug, pr.number!, body);
- }
- return;
- }
-
- // For repos migrated to main, close PRs opened against master.
- final bool isMaster = pr.base?.ref == 'master';
- final bool isMigrated = defaultBranchName == 'main';
- // PRs should never be open to "beta" or "stable."
- final bool isReleaseChannelBranch = releaseChannels.contains(pr.base?.ref);
- if ((isMaster && isMigrated) || isReleaseChannelBranch) {
- body = _getWrongBaseComment(base: baseName, defaultBranch: defaultBranchName);
- if (!await _alreadyCommented(gitHubClient, pr, body)) {
- await gitHubClient.pullRequests.edit(
- slug,
- pr.number!,
- base: Config.defaultBranch(slug),
- );
- await gitHubClient.issues.createComment(slug, pr.number!, body);
- }
- }
- }
-
- bool _isReleaseBranch(PullRequest pr) {
- final String defaultBranchName = Config.defaultBranch(pr.base!.repo!.slug());
- final String baseName = pr.base!.ref!;
-
- if (baseName == defaultBranchName) {
- return false;
- }
- // Check if branch name confroms to the format flutter-x.x-candidate.x,
- // A pr with conforming branch name is likely to be intended
- // for a release branch, whereas a pr with non conforming name is likely
- // caused by user misoperations, in which case bot
- // will suggest open pull request against default branch instead.
- final RegExp candidateTest = RegExp(r'flutter-\d+\.\d+-candidate\.\d+');
- if (candidateTest.hasMatch(baseName) && candidateTest.hasMatch(pr.head!.ref!)) {
- return true;
- }
- return false;
- }
-
- Future<bool> _alreadyCommented(
- GitHub gitHubClient,
- PullRequest pr,
- String message,
- ) async {
- final Stream<IssueComment> comments = gitHubClient.issues.listCommentsByIssue(pr.base!.repo!.slug(), pr.number!);
- await for (IssueComment comment in comments) {
- if (comment.body != null && comment.body!.contains(message)) {
- return true;
- }
- }
- return false;
- }
-
- String _getWrongBaseComment({
- required String base,
- required String defaultBranch,
- }) {
- final String messageTemplate = config.wrongBaseBranchPullRequestMessage;
- return messageTemplate.replaceAll('{{target_branch}}', base).replaceAll('{{default_branch}}', defaultBranch);
- }
-
- Future<PullRequestEvent?> _getPullRequestEvent(String request) async {
- try {
- return PullRequestEvent.fromJson(json.decode(request) as Map<String, dynamic>);
- } on FormatException {
- return null;
- }
- }
-
- /// Returns true if the changes to [file] are all code comments.
- ///
- /// If that cannot be determined with confidence, returns false. False
- /// negatives (e.g., for /* */-style multi-line comments) should be expected.
- bool _allChangesAreCodeComments(PullRequestFile file) {
- final int? linesAdded = file.additionsCount;
- final int? linesDeleted = file.deletionsCount;
- final String? patch = file.patch;
- // If information is missing, err or the side of assuming it's a non-comment
- // change.
- if (linesAdded == null || linesDeleted == null || patch == null) {
- return false;
- }
-
- final String filename = file.filename!;
- final String? extension = filename.contains('.') ? filename.split('.').last.toLowerCase() : null;
- if (extension == null || !knownCommentCodeExtensions.contains(extension)) {
- return false;
- }
-
- // Only handles single-line comments; identifying multi-line comments
- // would require the full file and non-trivial parsing. Also doesn't handle
- // end-of-line comments (e.g., "int x = 0; // Info about x").
- final RegExp commentRegex = RegExp(r'^[+-]\s*//');
- final RegExp onlyWhitespaceRegex = RegExp(r'^[+-]\s*$');
- for (String line in patch.split('\n')) {
- if (!line.startsWith('+') && !line.startsWith('-')) {
- continue;
- }
-
- if (onlyWhitespaceRegex.hasMatch(line)) {
- // whitespace only changes don't require tests
- continue;
- }
-
- if (!commentRegex.hasMatch(line)) {
- return false;
- }
- }
- return true;
- }
-}
diff --git a/app_dart/lib/src/request_handlers/github_rate_limit_status.dart b/app_dart/lib/src/request_handlers/github_rate_limit_status.dart
deleted file mode 100644
index d8cdc00..0000000
--- a/app_dart/lib/src/request_handlers/github_rate_limit_status.dart
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-
-import 'package:meta/meta.dart';
-
-import '../foundation/utils.dart';
-import '../request_handling/body.dart';
-import '../request_handling/request_handler.dart';
-import '../service/github_service.dart';
-import '../service/logging.dart';
-
-@immutable
-
-/// Endpoint to collect the current GitHub API quota usage of the flutter-dashboard app.
-///
-/// This endpoint pushes data to BigQuery for metric collection to analyze usage over time. There
-/// is a cron job set to run every minute, behind a [CacheRequestHandler] to ensure there exists
-/// at most one entry per repo per minute.
-///
-/// BigQuery entries contain the following fields:
-/// `timestamp`: [DateTime] of this entry.
-/// `limit`: Total API calls allowed on flutter-dashboard.
-/// `remaining`: Total number of API calls remaining before flutter-dashboard is blocked from sending further requests.
-/// `resets`: [DateTime] when [remaining] will reset back to [limit].
-class GithubRateLimitStatus extends RequestHandler<Body> {
- const GithubRateLimitStatus({
- required super.config,
- });
-
- @override
- Future<Body> get() async {
- final GithubService githubService = await config.createDefaultGitHubService();
- final Map<String, dynamic> quotaUsage = (await githubService.getRateLimit()).toJson();
- quotaUsage['timestamp'] = DateTime.now().toIso8601String();
-
- final int remainingQuota = quotaUsage['remaining'] as int;
- final int quotaLimit = quotaUsage['limit'] as int;
- const double githubQuotaUsageSLO = 0.5;
- if (remainingQuota < githubQuotaUsageSLO * quotaLimit) {
- log.warning(
- 'Remaining GitHub quota is $remainingQuota, which is less than quota usage SLO ${githubQuotaUsageSLO * quotaLimit} (${githubQuotaUsageSLO * 100}% of the limit $quotaLimit)).',
- );
- }
-
- /// Insert quota usage to BigQuery
- const String githubQuotaTable = 'GithubQuotaUsage';
- await insertBigquery(githubQuotaTable, quotaUsage, await config.createTabledataResourceApi());
- return Body.forJson(quotaUsage);
- }
-}
diff --git a/app_dart/lib/src/request_handlers/github_webhook.dart b/app_dart/lib/src/request_handlers/github_webhook.dart
deleted file mode 100644
index 0d3e1d6..0000000
--- a/app_dart/lib/src/request_handlers/github_webhook.dart
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-import 'dart:convert';
-import 'package:crypto/crypto.dart';
-import '../model/proto/protos.dart' as pb;
-import '../request_handling/body.dart';
-import '../request_handling/exceptions.dart';
-import '../request_handling/pubsub.dart';
-import '../request_handling/request_handler.dart';
-import '../service/logging.dart';
-
-/// The [RequestHandler] for processing GitHub webhooks and publishing valid events to PubSub.
-///
-/// Requests are only published as a [GithubWebhookMessage] iff they contain:
-/// 1. Event type from the header `X-GitHub-Event`
-/// 2. Event payload that was HMAC authenticated
-class GithubWebhook extends RequestHandler<Body> {
- GithubWebhook({
- required super.config,
- required this.pubsub,
- required this.secret,
- required this.topic,
- });
-
- final PubSub pubsub;
-
- /// PubSub topic to publish authenticated requests to.
- final String topic;
-
- /// Future that resolves to the GitHub apps webhook secret.
- final Future<String> secret;
-
- @override
- Future<Body> post() async {
- final String? event = request!.headers.value('X-GitHub-Event');
-
- if (event == null || request!.headers.value('X-Hub-Signature') == null) {
- throw const BadRequestException('Missing required headers.');
- }
- final List<int> requestBytes = await request!.expand((_) => _).toList();
- final String? hmacSignature = request!.headers.value('X-Hub-Signature');
- await _validateRequest(hmacSignature, requestBytes);
-
- final String requestString = utf8.decode(requestBytes);
-
- final pb.GithubWebhookMessage message = pb.GithubWebhookMessage.create()
- ..event = event
- ..payload = requestString;
- log.fine(message);
- await pubsub.publish(topic, message.writeToJsonMap());
-
- return Body.empty;
- }
-
- /// Ensures the signature provided for the given payload matches what is expected.
- ///
- /// The expected key is the sha1 hash of the payload using the private key of the GitHub app.
- Future<void> _validateRequest(
- String? signature,
- List<int> requestBody,
- ) async {
- final String rawKey = await secret;
- final List<int> key = utf8.encode(rawKey);
- final Hmac hmac = Hmac(sha1, key);
- final Digest digest = hmac.convert(requestBody);
- final String bodySignature = 'sha1=$digest';
- if (bodySignature != signature) {
- throw const Forbidden('X-Hub-Signature does not match expected value');
- }
- }
-}
diff --git a/app_dart/lib/src/request_handlers/postsubmit_luci_subscription.dart b/app_dart/lib/src/request_handlers/postsubmit_luci_subscription.dart
deleted file mode 100644
index 1a70e80..0000000
--- a/app_dart/lib/src/request_handlers/postsubmit_luci_subscription.dart
+++ /dev/null
@@ -1,137 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:cocoon_service/ci_yaml.dart';
-import 'package:gcloud/db.dart';
-import 'package:meta/meta.dart';
-
-import '../model/appengine/commit.dart';
-import '../model/appengine/task.dart';
-import '../model/luci/push_message.dart';
-import '../request_handling/body.dart';
-import '../request_handling/exceptions.dart';
-import '../request_handling/subscription_handler.dart';
-import '../service/datastore.dart';
-import '../service/logging.dart';
-import '../service/github_checks_service.dart';
-import '../service/scheduler.dart';
-
-/// An endpoint for listening to build updates for postsubmit builds.
-///
-/// The PubSub subscription is set up here:
-/// https://cloud.google.com/cloudpubsub/subscription/detail/luci-postsubmit?project=flutter-dashboard&tab=overview
-///
-/// This endpoint is responsible for updating Datastore with the result of builds from LUCI.
-@immutable
-class PostsubmitLuciSubscription extends SubscriptionHandler {
- /// Creates an endpoint for listening to LUCI status updates.
- const PostsubmitLuciSubscription({
- required super.cache,
- required super.config,
- super.authProvider,
- @visibleForTesting this.datastoreProvider = DatastoreService.defaultProvider,
- required this.scheduler,
- required this.githubChecksService,
- }) : super(subscriptionName: 'luci-postsubmit');
-
- final DatastoreServiceProvider datastoreProvider;
- final Scheduler scheduler;
- final GithubChecksService githubChecksService;
-
- @override
- Future<Body> post() async {
- final DatastoreService datastore = datastoreProvider(config.db);
-
- final BuildPushMessage buildPushMessage = BuildPushMessage.fromPushMessage(message);
- log.fine('userData=${buildPushMessage.userData}');
- log.fine('Updating buildId=${buildPushMessage.build?.id} for result=${buildPushMessage.build?.result}');
- if (buildPushMessage.userData.isEmpty) {
- log.fine('User data is empty');
- return Body.empty;
- }
-
- final String? rawTaskKey = buildPushMessage.userData['task_key'] as String?;
- final String? rawCommitKey = buildPushMessage.userData['commit_key'] as String?;
- if (rawCommitKey == null) {
- throw const BadRequestException('userData does not contain commit_key');
- }
- final Build? build = buildPushMessage.build;
- if (build == null) {
- log.warning('Build is null');
- return Body.empty;
- }
- final Key<String> commitKey = Key<String>(Key<dynamic>.emptyKey(Partition(null)), Commit, rawCommitKey);
- Task? task;
- if (rawTaskKey == null || rawTaskKey.isEmpty || rawTaskKey == 'null') {
- log.fine('Pulling builder name from parameters_json...');
- log.fine(build.buildParameters);
- final String? taskName = build.buildParameters?['builder_name'] as String?;
- if (taskName == null || taskName.isEmpty) {
- throw const BadRequestException('task_key is null and parameters_json does not contain the builder name');
- }
- final List<Task> tasks = await datastore.queryRecentTasksByName(name: taskName).toList();
- task = tasks.singleWhere((Task task) => task.parentKey?.id == commitKey.id);
- } else {
- log.fine('Looking up key...');
- final int taskId = int.parse(rawTaskKey);
- final Key<int> taskKey = Key<int>(commitKey, Task, taskId);
- task = await datastore.lookupByValue<Task>(taskKey);
- }
- log.fine('Found $task');
-
- if (_shouldUpdateTask(build, task)) {
- final String oldTaskStatus = task.status;
- task.updateFromBuild(build);
- await datastore.insert(<Task>[task]);
- log.fine('Updated datastore from $oldTaskStatus to ${task.status}');
- } else {
- log.fine('skip processing for build with status scheduled or task with status finished.');
- }
-
- final Commit commit = await datastore.lookupByValue<Commit>(commitKey);
- final CiYaml ciYaml = await scheduler.getCiYaml(commit);
- final List<Target> postsubmitTargets = ciYaml.postsubmitTargets;
- if (!postsubmitTargets.any((element) => element.value.name == task!.name)) {
- log.warning('Target ${task.name} has been deleted from TOT. Skip updating.');
- return Body.empty;
- }
- final Target target = postsubmitTargets.singleWhere((Target target) => target.value.name == task!.name);
- if (task.status == Task.statusFailed ||
- task.status == Task.statusInfraFailure ||
- task.status == Task.statusCancelled) {
- log.fine('Trying to auto-retry...');
- final bool retried = await scheduler.luciBuildService.checkRerunBuilder(
- commit: commit,
- target: target,
- task: task,
- datastore: datastore,
- );
- log.info('Retried: $retried');
- }
-
- // Only update GitHub checks if target is not bringup
- if (target.value.bringup == false && config.postsubmitSupportedRepos.contains(target.slug)) {
- log.info('Updating check status for ${target.getTestName}');
- await githubChecksService.updateCheckStatus(
- buildPushMessage,
- scheduler.luciBuildService,
- commit.slug,
- );
- }
-
- return Body.empty;
- }
-
- // No need to update task in datastore if
- // 1) the build is `scheduled`. Task is marked as `In Progress`
- // whenever scheduled, either from scheduler/backfiller/rerun. We need to update
- // task in datastore only for
- // a) `started`: update info like builder number.
- // b) `completed`: update info like status.
- // 2) the task is already completed.
- // The task may have been marked as completed from test framework via update-task-status API.
- bool _shouldUpdateTask(Build build, Task task) {
- return build.status != Status.scheduled && !Task.finishedStatusValues.contains(task.status);
- }
-}
diff --git a/app_dart/lib/src/request_handlers/presubmit_luci_subscription.dart b/app_dart/lib/src/request_handlers/presubmit_luci_subscription.dart
deleted file mode 100644
index 63d4841..0000000
--- a/app_dart/lib/src/request_handlers/presubmit_luci_subscription.dart
+++ /dev/null
@@ -1,139 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:github/github.dart';
-import 'package:meta/meta.dart';
-
-import '../model/appengine/commit.dart';
-import '../model/ci_yaml/ci_yaml.dart';
-import '../model/ci_yaml/target.dart';
-import '../model/luci/push_message.dart';
-import '../request_handling/authentication.dart';
-import '../request_handling/body.dart';
-import '../request_handling/subscription_handler.dart';
-import '../service/buildbucket.dart';
-import '../service/config.dart';
-import '../service/github_checks_service.dart';
-import '../service/logging.dart';
-import '../service/luci_build_service.dart';
-import '../service/scheduler.dart';
-
-/// An endpoint for listening to LUCI status updates for scheduled builds.
-///
-/// [ScheduleBuildRequest.notify] property is set to tell LUCI to use this
-/// PubSub topic. LUCI then publishes updates about build status to that topic,
-/// which we listen to on the github-updater subscription. When new messages
-/// arrive, they are posted to this web service.
-///
-/// The PubSub subscription is set up here:
-/// https://console.cloud.google.com/cloudpubsub/subscription/detail/github-updater?project=flutter-dashboard
-///
-/// This endpoint is responsible for updating GitHub with the status of
-/// completed builds from LUCI.
-@immutable
-class PresubmitLuciSubscription extends SubscriptionHandler {
- /// Creates an endpoint for listening to LUCI status updates.
- const PresubmitLuciSubscription({
- required super.cache,
- required super.config,
- required this.buildBucketClient,
- required this.scheduler,
- required this.luciBuildService,
- required this.githubChecksService,
- AuthenticationProvider? authProvider,
- }) : super(subscriptionName: 'github-updater');
-
- final BuildBucketClient buildBucketClient;
- final LuciBuildService luciBuildService;
- final GithubChecksService githubChecksService;
- final Scheduler scheduler;
-
- @override
- Future<Body> post() async {
- RepositorySlug slug;
- final BuildPushMessage buildPushMessage = BuildPushMessage.fromPushMessage(message);
- final Build build = buildPushMessage.build!;
- final String builderName = build.tagsByName('builder').single;
- log.fine('Available tags: ${build.tags.toString()}');
- // Skip status update if we can not get the sha tag.
- if (build.tagsByName('buildset').isEmpty) {
- log.warning('Buildset tag not included, skipping Status Updates');
- return Body.empty;
- }
- log.fine('Setting status: ${buildPushMessage.toJson()} for $builderName');
- if (buildPushMessage.userData.containsKey('repo_owner') && buildPushMessage.userData.containsKey('repo_name')) {
- // Message is coming from a github checks api enabled repo. We need to
- // create the slug from the data in the message and send the check status
- // update.
- slug = RepositorySlug(
- buildPushMessage.userData['repo_owner'] as String,
- buildPushMessage.userData['repo_name'] as String,
- );
- bool rescheduled = false;
- if (githubChecksService.taskFailed(buildPushMessage)) {
- final int currentAttempt = githubChecksService.currentAttempt(buildPushMessage);
- final int maxAttempt = await _getMaxAttempt(buildPushMessage, slug, builderName);
- if (currentAttempt < maxAttempt) {
- rescheduled = true;
- log.fine('Rerun a failed task: $builderName');
- await luciBuildService.rescheduleBuild(
- builderName: builderName,
- buildPushMessage: buildPushMessage,
- rescheduleAttempt: currentAttempt + 1,
- );
- }
- }
- await githubChecksService.updateCheckStatus(
- buildPushMessage,
- luciBuildService,
- slug,
- rescheduled: rescheduled,
- );
- } else {
- log.shout('This repo does not support checks API');
- }
- return Body.empty;
- }
-
- /// Gets target's allowed reschedule attempt.
- ///
- /// Each target can define their own allowed max number of reschedule attemp, and it
- /// is defined as a property `presubmit_max_attempts`.
- ///
- /// If not property is defined, the target doesn't allow a reschedule after failures.
- /// Typically the property will be used for targets that are likely flaky.
- Future<int> _getMaxAttempt(
- BuildPushMessage buildPushMessage,
- RepositorySlug slug,
- String builderName,
- ) async {
- final Commit commit = Commit(
- branch: buildPushMessage.userData['commit_branch'] as String,
- repository: slug.fullName,
- sha: buildPushMessage.userData['commit_sha'] as String,
- );
- late CiYaml ciYaml;
- if (commit.branch == Config.defaultBranch(commit.slug)) {
- ciYaml = await scheduler.getCiYaml(commit, validate: true);
- } else {
- ciYaml = await scheduler.getCiYaml(commit);
- }
-
- // Do not block on the target not found.
- if (!ciYaml.presubmitTargets.any((element) => element.value.name == builderName)) {
- // do not reschedule
- log.warning('Did not find builder with name: $builderName in ciYaml for ${commit.sha}');
- final List<String> availableBuilderList = ciYaml.presubmitTargets.map((Target e) => e.value.name).toList();
- log.warning('ciYaml presubmit targets found: $availableBuilderList');
- return 1;
- }
-
- final Target target = ciYaml.presubmitTargets.where((element) => element.value.name == builderName).single;
- final Map<String, Object> properties = target.getProperties();
- if (!properties.containsKey('presubmit_max_attempts')) {
- return 1;
- }
- return properties['presubmit_max_attempts'] as int;
- }
-}
diff --git a/app_dart/lib/src/request_handlers/push_build_status_to_github.dart b/app_dart/lib/src/request_handlers/push_build_status_to_github.dart
deleted file mode 100644
index 7c0de91..0000000
--- a/app_dart/lib/src/request_handlers/push_build_status_to_github.dart
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-
-import 'package:github/github.dart';
-import 'package:meta/meta.dart';
-
-import '../../cocoon_service.dart';
-import '../model/appengine/github_build_status_update.dart';
-import '../request_handling/api_request_handler.dart';
-import '../service/build_status_provider.dart';
-import '../service/datastore.dart';
-import '../service/logging.dart';
-
-@immutable
-class PushBuildStatusToGithub extends ApiRequestHandler<Body> {
- const PushBuildStatusToGithub({
- required super.config,
- required super.authenticationProvider,
- @visibleForTesting DatastoreServiceProvider? datastoreProvider,
- @visibleForTesting BuildStatusServiceProvider? buildStatusServiceProvider,
- }) : datastoreProvider = datastoreProvider ?? DatastoreService.defaultProvider,
- buildStatusServiceProvider = buildStatusServiceProvider ?? BuildStatusService.defaultProvider;
-
- final BuildStatusServiceProvider buildStatusServiceProvider;
- final DatastoreServiceProvider datastoreProvider;
- static const String fullNameRepoParam = 'repo';
-
- @override
- Future<Body> get() async {
- if (authContext!.clientContext.isDevelopmentEnvironment) {
- // Don't push GitHub status from the local dev server.
- log.fine('GitHub statuses are not pushed from local dev environments');
- return Body.empty;
- }
-
- final String repository = request!.uri.queryParameters[fullNameRepoParam] ?? 'flutter/flutter';
- final RepositorySlug slug = RepositorySlug.full(repository);
- final DatastoreService datastore = datastoreProvider(config.db);
- final BuildStatusService buildStatusService = buildStatusServiceProvider(datastore);
-
- final BuildStatus status = (await buildStatusService.calculateCumulativeStatus(slug))!;
- await _insertBigquery(slug, status.githubStatus, Config.defaultBranch(slug), config);
- await _updatePRs(slug, status.githubStatus, datastore);
- log.fine('All the PRs for $repository have been updated with $status');
-
- return Body.empty;
- }
-
- Future<void> _updatePRs(RepositorySlug slug, String status, DatastoreService datastore) async {
- final GitHub github = await config.createGitHubClient(slug: slug);
- final List<GithubBuildStatusUpdate> updates = <GithubBuildStatusUpdate>[];
- await for (PullRequest pr in github.pullRequests.list(slug)) {
- // Tree status is only put on PRs merging into ToT.
- if (pr.base!.ref != Config.defaultBranch(slug)) {
- log.fine('This PR is not staged to land on ${Config.defaultBranch(slug)}, skipping.');
- continue;
- }
- final GithubBuildStatusUpdate update = await datastore.queryLastStatusUpdate(slug, pr);
- if (update.status != status) {
- log.fine('Updating status of ${slug.fullName}#${pr.number} from ${update.status} to $status');
- final CreateStatus request = CreateStatus(status);
- request.targetUrl = 'https://flutter-dashboard.appspot.com/#/build?repo=${slug.name}';
- request.context = 'tree-status';
- if (status != GithubBuildStatusUpdate.statusSuccess) {
- request.description = config.flutterBuildDescription;
- }
- try {
- await github.repositories.createStatus(slug, pr.head!.sha!, request);
- update.status = status;
- update.updates = (update.updates ?? 0) + 1;
- update.updateTimeMillis = DateTime.now().millisecondsSinceEpoch;
- updates.add(update);
- } catch (error) {
- log.severe('Failed to post status update to ${slug.fullName}#${pr.number}: $error');
- }
- }
- }
- await datastore.insert(updates);
- }
-
- Future<void> _insertBigquery(RepositorySlug slug, String status, String branch, Config config) async {
- const String bigqueryTableName = 'BuildStatus';
- final Map<String, dynamic> bigqueryData = <String, dynamic>{
- 'Timestamp': DateTime.now().millisecondsSinceEpoch,
- 'Status': status,
- 'Branch': branch,
- 'Repo': slug.name,
- };
- await insertBigquery(bigqueryTableName, bigqueryData, await config.createTabledataResourceApi());
- }
-}
diff --git a/app_dart/lib/src/request_handlers/push_gold_status_to_github.dart b/app_dart/lib/src/request_handlers/push_gold_status_to_github.dart
deleted file mode 100644
index d3fdcd9..0000000
--- a/app_dart/lib/src/request_handlers/push_gold_status_to_github.dart
+++ /dev/null
@@ -1,376 +0,0 @@
-// Copyright 2020 The Flutter 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:async';
-import 'dart:convert';
-import 'dart:io';
-
-import 'package:github/github.dart';
-import 'package:gql/language.dart' as lang;
-import 'package:graphql/client.dart';
-import 'package:http/http.dart' as http;
-import 'package:meta/meta.dart';
-
-import '../model/appengine/github_gold_status_update.dart';
-import '../request_handling/api_request_handler.dart';
-import '../request_handling/body.dart';
-import '../request_handling/exceptions.dart';
-import '../service/config.dart';
-import '../service/datastore.dart';
-import '../service/logging.dart';
-
-@immutable
-class PushGoldStatusToGithub extends ApiRequestHandler<Body> {
- PushGoldStatusToGithub({
- required super.config,
- required super.authenticationProvider,
- @visibleForTesting DatastoreServiceProvider? datastoreProvider,
- http.Client? goldClient,
- this.ingestionDelay = const Duration(seconds: 10),
- }) : datastoreProvider = datastoreProvider ?? DatastoreService.defaultProvider,
- goldClient = goldClient ?? http.Client();
-
- final DatastoreServiceProvider datastoreProvider;
- final http.Client goldClient;
- final Duration ingestionDelay;
-
- @override
- Future<Body> get() async {
- final DatastoreService datastore = datastoreProvider(config.db);
-
- if (authContext!.clientContext.isDevelopmentEnvironment) {
- // Don't push gold status from the local dev server.
- return Body.empty;
- }
-
- await _sendStatusUpdates(datastore, Config.flutterSlug);
- await _sendStatusUpdates(datastore, Config.engineSlug);
-
- return Body.empty;
- }
-
- Future<void> _sendStatusUpdates(
- DatastoreService datastore,
- RepositorySlug slug,
- ) async {
- final GitHub gitHubClient = await config.createGitHubClient(slug: slug);
- final List<GithubGoldStatusUpdate> statusUpdates = <GithubGoldStatusUpdate>[];
- log.fine('Beginning Gold checks...');
- await for (PullRequest pr in gitHubClient.pullRequests.list(slug)) {
- assert(pr.number != null);
- // Get last known Gold status from datastore.
- final GithubGoldStatusUpdate lastUpdate = await datastore.queryLastGoldUpdate(slug, pr);
- CreateStatus statusRequest;
-
- log.fine('Last known Gold status for $slug#${pr.number} was with sha: '
- '${lastUpdate.head}, status: ${lastUpdate.status}, description: ${lastUpdate.description}');
-
- if (lastUpdate.status == GithubGoldStatusUpdate.statusCompleted && lastUpdate.head == pr.head!.sha) {
- log.fine('Completed status already reported for this commit.');
- // We have already seen this commit and it is completed or, this is not
- // a change staged to land on master, which we should ignore.
- continue;
- }
-
- final String defaultBranch = Config.defaultBranch(slug);
- if (pr.base!.ref != defaultBranch) {
- log.fine('This change is not staged to land on $defaultBranch, skipping.');
- // This is potentially a release branch, or another change not landing
- // on master, we don't need a Gold check.
- continue;
- }
-
- if (pr.draft!) {
- log.fine('This pull request is a draft.');
- // We don't want to query Gold while a PR is in a draft state, and we
- // don't want to needlessly hold a pending state either.
- // If a PR has been marked `draft` after the fact, and there has not
- // been a new commit, we cannot rescind a previously posted status, so
- // if it is already pending, we should make the contributor aware of
- // that fact.
- if (lastUpdate.status == GithubGoldStatusUpdate.statusRunning &&
- lastUpdate.head == pr.head!.sha &&
- !await _alreadyCommented(gitHubClient, pr, slug, config.flutterGoldDraftChange)) {
- await gitHubClient.issues
- .createComment(slug, pr.number!, config.flutterGoldDraftChange + config.flutterGoldAlertConstant(slug));
- }
- continue;
- }
-
- log.fine('Querying builds for pull request #${pr.number} with sha: ${lastUpdate.head}...');
- final GraphQLClient gitHubGraphQLClient = await config.createGitHubGraphQLClient();
- final List<String> incompleteChecks = <String>[];
- bool runsGoldenFileTests = false;
- final Map<String, dynamic> data = (await _queryGraphQL(
- gitHubGraphQLClient,
- slug,
- pr.number!,
- ))!;
- final Map<String, dynamic> prData = data['repository']['pullRequest'] as Map<String, dynamic>;
- final Map<String, dynamic> commit = prData['commits']['nodes'].single['commit'] as Map<String, dynamic>;
- List<Map<String, dynamic>>? checkRuns;
- if (commit['checkSuites']['nodes'] != null && (commit['checkSuites']['nodes'] as List<dynamic>).isNotEmpty) {
- checkRuns =
- (commit['checkSuites']['nodes']?.first['checkRuns']['nodes'] as List<dynamic>).cast<Map<String, dynamic>>();
- }
- checkRuns = checkRuns ?? <Map<String, dynamic>>[];
- log.fine('This PR has ${checkRuns.length} checks.');
- for (Map<String, dynamic> checkRun in checkRuns) {
- log.fine('Check run: $checkRun');
- final String name = checkRun['name'].toLowerCase() as String;
- // Framework shards run framework goldens
- // Web shards run web version of framework goldens
- // Misc shard runs API docs goldens
- if (name.contains('framework') || name.contains('web engine') || name.contains('misc')) {
- runsGoldenFileTests = true;
- }
- if (checkRun['conclusion'] == null || checkRun['conclusion'].toUpperCase() != 'SUCCESS') {
- incompleteChecks.add(name);
- }
- }
-
- if (runsGoldenFileTests) {
- log.fine('This PR executes golden file tests.');
- // Check when this PR was last updated. Gold does not keep results after
- // >20 days. If a PR has gone stale, we should draw attention to it to be
- // updated or closed.
- final DateTime updatedAt = pr.updatedAt!.toUtc();
- final DateTime twentyDaysAgo = DateTime.now().toUtc().subtract(const Duration(days: 20));
- if (updatedAt.isBefore(twentyDaysAgo)) {
- log.fine('Stale PR, no gold status to report.');
- if (!await _alreadyCommented(gitHubClient, pr, slug, config.flutterGoldStalePR)) {
- log.fine('Notifying for stale PR.');
- await gitHubClient.issues
- .createComment(slug, pr.number!, config.flutterGoldStalePR + config.flutterGoldAlertConstant(slug));
- }
- continue;
- }
-
- if (incompleteChecks.isNotEmpty) {
- // If checks on an open PR are running or failing, the gold status
- // should just be pending. Any draft PRs are skipped
- // until marked ready for review.
- log.fine('Waiting for checks to be completed.');
- statusRequest =
- _createStatus(GithubGoldStatusUpdate.statusRunning, config.flutterGoldPending, slug, pr.number!);
- } else {
- // We do not want to query Gold on a draft PR.
- assert(!pr.draft!);
- // Get Gold status.
- final String goldStatus = await _getGoldStatus(slug, pr);
- statusRequest = _createStatus(
- goldStatus,
- goldStatus == GithubGoldStatusUpdate.statusRunning ? config.flutterGoldChanges : config.flutterGoldSuccess,
- slug,
- pr.number!,
- );
- log.fine('New status for potential update: ${statusRequest.state}, ${statusRequest.description}');
- if (goldStatus == GithubGoldStatusUpdate.statusRunning &&
- !await _alreadyCommented(gitHubClient, pr, slug, config.flutterGoldCommentID(pr))) {
- log.fine('Notifying for triage.');
- await _commentAndApplyGoldLabels(gitHubClient, pr, slug);
- }
- }
-
- // Push updates if there is a status change (detected by unique description)
- // or this is a new commit.
- if (lastUpdate.description != statusRequest.description || lastUpdate.head != pr.head!.sha) {
- try {
- log.fine('Pushing status to GitHub: ${statusRequest.state}, ${statusRequest.description}');
- await gitHubClient.repositories.createStatus(slug, pr.head!.sha!, statusRequest);
- lastUpdate.status = statusRequest.state!;
- lastUpdate.head = pr.head!.sha;
- lastUpdate.updates = (lastUpdate.updates ?? 0) + 1;
- lastUpdate.description = statusRequest.description!;
- statusUpdates.add(lastUpdate);
- } catch (error) {
- log.severe('Failed to post status update to ${slug.fullName}#${pr.number}: $error');
- }
- }
- } else {
- log.fine('This PR does not execute golden file tests.');
- }
- }
- await datastore.insert(statusUpdates);
- log.fine('Committed all updates for $slug');
- }
-
- /// Returns a GitHub Status for the given state and description.
- CreateStatus _createStatus(String state, String description, RepositorySlug slug, int prNumber) {
- final CreateStatus statusUpdate = CreateStatus(state)
- ..targetUrl = _getTriageUrl(slug, prNumber)
- ..context = 'flutter-gold'
- ..description = description;
- return statusUpdate;
- }
-
- /// Used to check for any tryjob results from Flutter Gold associated with a
- /// pull request.
- Future<String> _getGoldStatus(RepositorySlug slug, PullRequest pr) async {
- // We wait for a few seconds in case tests _just_ finished and the tryjob
- // has not finished ingesting the results.
- await Future<void>.delayed(ingestionDelay);
- final Uri requestForTryjobStatus =
- Uri.parse('${_getGoldHost(slug)}/json/v1/changelist_summary/github/${pr.number}');
- try {
- log.fine('Querying Gold for image results...');
- final http.Response response = await goldClient.get(requestForTryjobStatus);
- if (response.statusCode != HttpStatus.ok) {
- throw HttpException(response.body);
- }
-
- final dynamic jsonResponseTriage = json.decode(response.body);
- if (jsonResponseTriage is! Map<String, dynamic>) {
- throw const FormatException('Skia gold changelist summary does not match expected format.');
- }
- final List<dynamic> patchsets = jsonResponseTriage['patchsets'] as List<dynamic>;
- int untriaged = 0;
- for (int i = 0; i < patchsets.length; i++) {
- final Map<String, dynamic> patchset = patchsets[i] as Map<String, dynamic>;
- if (patchset['patchset_id'] == pr.head!.sha) {
- untriaged = patchset['new_untriaged_images'] as int;
- break;
- }
- }
-
- if (untriaged == 0) {
- log.fine('There are no unexpected image results for #${pr.number} at sha '
- '${pr.head!.sha}.');
-
- return GithubGoldStatusUpdate.statusCompleted;
- } else {
- log.fine('Tryjob for #${pr.number} at sha ${pr.head!.sha} generated new '
- 'images.');
-
- return GithubGoldStatusUpdate.statusRunning;
- }
- } on FormatException catch (e) {
- throw BadRequestException('Formatting error detected requesting '
- 'tryjob status for pr #${pr.number} from Flutter Gold.\n'
- 'response: $response\n'
- 'error: $e');
- } catch (e) {
- throw BadRequestException('Error detected requesting tryjob status for pr '
- '#${pr.number} from Flutter Gold.\n'
- 'error: $e');
- }
- }
-
- String _getTriageUrl(RepositorySlug slug, int number) {
- return '${_getGoldHost(slug)}/cl/github/$number';
- }
-
- String _getGoldHost(RepositorySlug slug) {
- if (slug == Config.flutterSlug) {
- return 'https://flutter-gold.skia.org';
- }
-
- if (slug == Config.engineSlug) {
- return 'https://flutter-engine-gold.skia.org';
- }
-
- throw Exception('Unknown slug: $slug');
- }
-
- /// Creates a comment on a given pull request identified to have golden file
- /// changes and applies the `will affect goldens` label.
- Future<void> _commentAndApplyGoldLabels(
- GitHub gitHubClient,
- PullRequest pr,
- RepositorySlug slug,
- ) async {
- String body;
- if (await _isFirstComment(gitHubClient, pr, slug)) {
- body = config.flutterGoldInitialAlert(_getTriageUrl(slug, pr.number!));
- } else {
- body = config.flutterGoldFollowUpAlert(_getTriageUrl(slug, pr.number!));
- }
- body += config.flutterGoldAlertConstant(slug) + config.flutterGoldCommentID(pr);
- await gitHubClient.issues.createComment(slug, pr.number!, body);
- await gitHubClient.issues.addLabelsToIssue(slug, pr.number!, <String>[
- 'will affect goldens',
- ]);
- }
-
- Future<bool> _alreadyCommented(
- GitHub gitHubClient,
- PullRequest pr,
- RepositorySlug slug,
- String message,
- ) async {
- final Stream<IssueComment> comments = gitHubClient.issues.listCommentsByIssue(slug, pr.number!);
- await for (IssueComment comment in comments) {
- if (comment.body!.contains(message)) {
- return true;
- }
- }
- return false;
- }
-
- Future<bool> _isFirstComment(
- GitHub gitHubClient,
- PullRequest pr,
- RepositorySlug slug,
- ) async {
- final Stream<IssueComment> comments = gitHubClient.issues.listCommentsByIssue(slug, pr.number!);
- await for (IssueComment comment in comments) {
- if (comment.body!.contains(config.flutterGoldInitialAlert(_getTriageUrl(slug, pr.number!)))) {
- return false;
- }
- }
- return true;
- }
-}
-
-Future<Map<String, dynamic>?> _queryGraphQL(
- GraphQLClient client,
- RepositorySlug slug,
- int prNumber,
-) async {
- final QueryResult result = await client.query(
- QueryOptions(
- document: lang.parseString(pullRequestChecksQuery),
- fetchPolicy: FetchPolicy.noCache,
- variables: <String, dynamic>{
- 'sPullRequest': prNumber,
- 'sRepoOwner': slug.owner,
- 'sRepoName': slug.name,
- },
- ),
- );
-
- if (result.hasException) {
- log.severe(result.exception.toString());
- throw const BadRequestException('GraphQL query failed');
- }
- return result.data;
-}
-
-const String pullRequestChecksQuery = r'''
-query ChecksForPullRequest($sPullRequest: Int!, $sRepoOwner: String!, $sRepoName: String!) {
- repository(owner: $sRepoOwner, name: $sRepoName) {
- pullRequest(number: $sPullRequest) {
- commits(last: 1) {
- nodes {
- commit {
- # (appId: 64368) == flutter-dashboard. We only care about
- # flutter-dashboard checks.
-
- checkSuites(last: 1, filterBy: {appId: 64368}) {
- nodes {
- checkRuns(first: 100) {
- nodes {
- name
- status
- conclusion
- }
- }
- }
- }
- }
- }
- }
- }
- }
-}''';
diff --git a/app_dart/lib/src/request_handlers/readiness_check.dart b/app_dart/lib/src/request_handlers/readiness_check.dart
deleted file mode 100644
index 6c8ecbe..0000000
--- a/app_dart/lib/src/request_handlers/readiness_check.dart
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2021 The Flutter 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:async';
-import 'package:meta/meta.dart';
-import '../request_handling/body.dart';
-import '../request_handling/request_handler.dart';
-
-@immutable
-class ReadinessCheck extends RequestHandler<Body> {
- const ReadinessCheck({required super.config});
-
- @override
- Future<Body> get() async {
- return Body.empty;
- }
-}
diff --git a/app_dart/lib/src/request_handlers/reset_prod_task.dart b/app_dart/lib/src/request_handlers/reset_prod_task.dart
deleted file mode 100644
index 19e3162..0000000
--- a/app_dart/lib/src/request_handlers/reset_prod_task.dart
+++ /dev/null
@@ -1,191 +0,0 @@
-// Copyright 2020 The Flutter 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:async';
-
-import 'package:gcloud/db.dart';
-import 'package:github/github.dart';
-import 'package:meta/meta.dart';
-
-import '../model/appengine/commit.dart';
-import '../model/appengine/key_helper.dart';
-import '../model/appengine/task.dart';
-import '../model/ci_yaml/ci_yaml.dart';
-import '../model/ci_yaml/target.dart';
-import '../model/google/token_info.dart';
-import '../request_handling/api_request_handler.dart';
-import '../request_handling/body.dart';
-import '../request_handling/exceptions.dart';
-import '../service/config.dart';
-import '../service/datastore.dart';
-import '../service/luci_build_service.dart';
-import '../service/scheduler.dart';
-
-/// Reruns a postsubmit LUCI build.
-///
-/// Expects either [taskKeyParam] or a set of params that give enough detail to lookup a task in datastore.
-@immutable
-class ResetProdTask extends ApiRequestHandler<Body> {
- const ResetProdTask({
- required super.config,
- required super.authenticationProvider,
- required this.luciBuildService,
- required this.scheduler,
- @visibleForTesting DatastoreServiceProvider? datastoreProvider,
- }) : datastoreProvider = datastoreProvider ?? DatastoreService.defaultProvider;
-
- final DatastoreServiceProvider datastoreProvider;
- final LuciBuildService luciBuildService;
- final Scheduler scheduler;
-
- static const String branchParam = 'Branch';
- static const String taskKeyParam = 'Key';
- static const String ownerParam = 'Owner';
- static const String repoParam = 'Repo';
- static const String commitShaParam = 'Commit';
-
- /// Name of the task to be retried.
- ///
- /// If "all" is given, all failed tasks will be retried. This enables
- /// oncalls to quickly recover a commit without the tedium of the UI.
- static const String taskParam = 'Task';
-
- @override
- Future<Body> post() async {
- final DatastoreService datastore = datastoreProvider(config.db);
- final String? encodedKey = requestData![taskKeyParam] as String?;
- String? branch = requestData![branchParam] as String?;
- final String owner = requestData![ownerParam] as String? ?? 'flutter';
- final String? repo = requestData![repoParam] as String?;
- final String? sha = requestData![commitShaParam] as String?;
- final TokenInfo token = await tokenInfo(request!);
- final String? taskName = requestData![taskParam] as String?;
-
- RepositorySlug? slug;
- if (encodedKey != null && encodedKey.isNotEmpty) {
- // Check params required for dashboard.
- checkRequiredParameters(<String>[taskKeyParam]);
- } else {
- // Checks params required when this API is called with curl.
- checkRequiredParameters(<String>[commitShaParam, taskParam, repoParam]);
- slug = RepositorySlug(owner, repo!);
- branch ??= Config.defaultBranch(slug);
- }
-
- if (taskName == 'all') {
- final Key<String> commitKey = Commit.createKey(
- db: datastore.db,
- slug: slug!,
- gitBranch: branch!,
- sha: sha!,
- );
- final tasks = await datastore.db.query<Task>(ancestorKey: commitKey).run().toList();
- final List<Future<void>> futures = <Future<void>>[];
- for (final Task task in tasks) {
- if (!taskFailStatusSet.contains(task.status)) continue;
- futures.add(
- rerun(
- datastore: datastore,
- branch: branch,
- sha: sha,
- taskName: task.name,
- slug: slug,
- email: token.email!,
- ),
- );
- }
- await Future.wait(futures);
- } else {
- await rerun(
- datastore: datastore,
- encodedKey: encodedKey,
- branch: branch,
- sha: sha,
- taskName: taskName,
- slug: slug,
- email: token.email!,
- ignoreChecks: true,
- );
- }
-
- return Body.empty;
- }
-
- Future<void> rerun({
- required DatastoreService datastore,
- String? encodedKey,
- String? branch,
- String? sha,
- String? taskName,
- RepositorySlug? slug,
- required String email,
- bool ignoreChecks = false,
- }) async {
- final Task task = await _getTaskFromNamedParams(
- datastore: datastore,
- encodedKey: encodedKey,
- branch: branch,
- name: taskName,
- sha: sha,
- slug: slug,
- );
- final Commit commit = await _getCommitFromTask(datastore, task);
-
- final CiYaml ciYaml = await scheduler.getCiYaml(commit);
- final Target target = ciYaml.postsubmitTargets.singleWhere((Target target) => target.value.name == task.name);
-
- final Map<String, List<String>> tags = <String, List<String>>{
- 'triggered_by': <String>[email],
- 'trigger_type': <String>['manual'],
- };
- final bool isRerunning = await luciBuildService.checkRerunBuilder(
- commit: commit,
- task: task,
- target: target,
- datastore: datastore,
- tags: tags,
- ignoreChecks: ignoreChecks,
- );
-
- // For human retries from the dashboard, notify if a task failed to rerun.
- if (ignoreChecks && isRerunning == false) {
- throw InternalServerError('Failed to rerun $taskName');
- }
- }
-
- /// Retrieve [Task] from [DatastoreService] from either an encoded key or commit + task name info.
- ///
- /// If [encodedKey] is passed, [KeyHelper] will decode it directly and return the associated entity.
- ///
- /// Otherwise, [name], [branch], [sha], and [slug] must be passed to find the [Task].
- Future<Task> _getTaskFromNamedParams({
- required DatastoreService datastore,
- String? encodedKey,
- String? branch,
- String? name,
- String? sha,
- RepositorySlug? slug,
- }) async {
- if (encodedKey != null && encodedKey.isNotEmpty) {
- final Key<int> key = config.keyHelper.decode(encodedKey) as Key<int>;
- return datastore.lookupByValue<Task>(key);
- }
- final Key<String> commitKey = Commit.createKey(
- db: datastore.db,
- slug: slug!,
- gitBranch: branch!,
- sha: sha!,
- );
- return Task.fromDatastore(
- datastore: datastore,
- commitKey: commitKey,
- name: name!,
- );
- }
-
- /// Returns the [Commit] associated with [Task].
- Future<Commit> _getCommitFromTask(DatastoreService datastore, Task task) async {
- return (await datastore.lookupByKey<Commit>(<Key<dynamic>>[task.parentKey!])).single!;
- }
-}
diff --git a/app_dart/lib/src/request_handlers/reset_try_task.dart b/app_dart/lib/src/request_handlers/reset_try_task.dart
deleted file mode 100644
index 19829a5..0000000
--- a/app_dart/lib/src/request_handlers/reset_try_task.dart
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2020 The Flutter 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:async';
-
-import 'package:github/github.dart';
-import 'package:meta/meta.dart';
-
-import '../../cocoon_service.dart';
-import '../request_handling/api_request_handler.dart';
-import '../request_handling/exceptions.dart';
-
-/// Runs all the applicable tasks for a given PR and commit hash. This will be
-/// used to unblock rollers when creating a new commit is not possible.
-@immutable
-class ResetTryTask extends ApiRequestHandler<Body> {
- const ResetTryTask({
- required super.config,
- required super.authenticationProvider,
- required this.scheduler,
- });
-
- final Scheduler scheduler;
-
- static const String kOwnerParam = 'owner';
- static const String kRepoParam = 'repo';
- static const String kPullRequestNumberParam = 'pr';
- static const String kBuilderParam = 'builders';
-
- @override
- Future<Body> get() async {
- checkRequiredQueryParameters(<String>[kRepoParam, kPullRequestNumberParam]);
- final String owner = request!.uri.queryParameters[kOwnerParam] ?? 'flutter';
- final String repo = request!.uri.queryParameters[kRepoParam]!;
- final String pr = request!.uri.queryParameters[kPullRequestNumberParam]!;
- final String builders = request!.uri.queryParameters[kBuilderParam] ?? '';
- final List<String> builderList = getBuilderList(builders);
-
- final int? prNumber = int.tryParse(pr);
- if (prNumber == null) {
- throw const BadRequestException('$kPullRequestNumberParam must be a number');
- }
- final RepositorySlug slug = RepositorySlug(owner, repo);
- final GitHub github = await config.createGitHubClient(slug: slug);
- final PullRequest pullRequest = await github.pullRequests.get(slug, prNumber);
- await scheduler.triggerPresubmitTargets(pullRequest: pullRequest, builderTriggerList: builderList);
- return Body.empty;
- }
-
- /// Parses [builders] to a String list.
- ///
- /// The [builders] parameter is expecting comma joined string, e.g. 'builder1, builder2'.
- /// Returns an empty list if no [builders] is specified.
- List<String> getBuilderList(String builders) {
- if (builders.isEmpty) {
- return <String>[];
- }
- return builders.split(',').map((String builder) => builder.trim()).toList();
- }
-}
diff --git a/app_dart/lib/src/request_handlers/scheduler/batch_backfiller.dart b/app_dart/lib/src/request_handlers/scheduler/batch_backfiller.dart
deleted file mode 100644
index 0c80599..0000000
--- a/app_dart/lib/src/request_handlers/scheduler/batch_backfiller.dart
+++ /dev/null
@@ -1,245 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:cocoon_service/src/foundation/utils.dart';
-import 'package:cocoon_service/src/model/appengine/task.dart';
-import 'package:cocoon_service/src/request_handling/body.dart';
-import 'package:cocoon_service/src/service/datastore.dart';
-import 'package:cocoon_service/src/service/scheduler/policy.dart';
-import 'package:gcloud/db.dart';
-import 'package:github/github.dart';
-import 'package:meta/meta.dart';
-import 'package:retry/retry.dart';
-
-import '../../model/ci_yaml/ci_yaml.dart';
-import '../../model/ci_yaml/target.dart';
-import '../../request_handling/exceptions.dart';
-import '../../request_handling/request_handler.dart';
-import '../../service/config.dart';
-import '../../service/logging.dart';
-import '../../service/luci_build_service.dart';
-import '../../service/scheduler.dart';
-
-/// Cron request handler for scheduling targets when capacity becomes available.
-///
-/// Targets that have a [BatchPolicy] need to have backfilling enabled to ensure that ToT is always being tested.
-@immutable
-class BatchBackfiller extends RequestHandler {
- /// Creates a subscription for sending BuildBucket requests.
- const BatchBackfiller({
- required super.config,
- required this.scheduler,
- @visibleForTesting this.datastoreProvider = DatastoreService.defaultProvider,
- });
-
- final DatastoreServiceProvider datastoreProvider;
- final Scheduler scheduler;
-
- @override
- Future<Body> get() async {
- final List<Future<void>> futures = <Future<void>>[];
-
- for (RepositorySlug slug in config.supportedRepos) {
- futures.add(backfillRepository(slug));
- }
-
- // Process all repos asynchronously
- await Future.wait<void>(futures);
-
- return Body.empty;
- }
-
- Future<void> backfillRepository(RepositorySlug slug) async {
- final DatastoreService datastore = datastoreProvider(config.db);
- final List<FullTask> tasks =
- await (datastore.queryRecentTasks(slug: slug, commitLimit: config.backfillerCommitLimit)).toList();
-
- // Construct Task columns to scan for backfilling
- final Map<String, List<FullTask>> taskMap = <String, List<FullTask>>{};
- for (FullTask fullTask in tasks) {
- if (taskMap.containsKey(fullTask.task.name)) {
- taskMap[fullTask.task.name]!.add(fullTask);
- } else {
- taskMap[fullTask.task.name!] = <FullTask>[fullTask];
- }
- }
-
- // Check if should be scheduled (there is no yellow runs). Run the most recent gray.
- List<Tuple<Target, FullTask, int>> backfill = <Tuple<Target, FullTask, int>>[];
- for (List<FullTask> taskColumn in taskMap.values) {
- final FullTask task = taskColumn.first;
- final CiYaml ciYaml = await scheduler.getCiYaml(task.commit);
- final List<Target> ciYamlTargets = ciYaml.backfillTargets;
- // Skips scheduling if the task is not in TOT commit anymore.
- final bool taskInToT = ciYamlTargets.map((Target target) => target.value.name).toList().contains(task.task.name);
- if (!taskInToT) {
- continue;
- }
- final Target target = ciYamlTargets.singleWhere((target) => target.value.name == task.task.name);
- if (target.schedulerPolicy is! BatchPolicy) {
- continue;
- }
- final FullTask? backfillTask = _backfillTask(target, taskColumn);
- final int? priority = backfillPriority(taskColumn.map((e) => e.task).toList());
- if (priority != null && backfillTask != null) {
- backfill.add(Tuple<Target, FullTask, int>(target, backfillTask, priority));
- }
- }
-
- // Get the number of targets to be backfilled in each cycle.
- backfill = getFilteredBackfill(backfill);
-
- log.fine('Backfilling ${backfill.length} builds');
- log.fine(backfill.map<String>((Tuple<Target, FullTask, int> tuple) => tuple.first.value.name));
-
- // Update tasks status as in progress to avoid duplicate scheduling.
- final List<Task> backfillTasks = backfill.map((Tuple<Target, FullTask, int> tuple) => tuple.second.task).toList();
- try {
- await datastore.withTransaction<void>((Transaction transaction) async {
- transaction.queueMutations(inserts: backfillTasks);
- await transaction.commit();
- log.fine(
- 'Updated ${backfillTasks.length} tasks: ${backfillTasks.map((e) => e.name).toList()} when backfilling.',
- );
- });
- // Schedule all builds asynchronously.
- // Schedule after db updates to avoid duplicate scheduling when db update fails.
- await _scheduleWithRetries(backfill);
- } catch (error) {
- log.severe('Failed to update tasks when backfilling: $error');
- }
- }
-
- /// Filters [config.backfillerTargetLimit] targets to backfill.
- ///
- /// High priority targets will be guranteed to get back filled first. If more targets
- /// than [config.backfillerTargetLimit], pick the limited number of targets after a
- /// shuffle. This is to make sure all targets are picked with the same chance.
- List<Tuple<Target, FullTask, int>> getFilteredBackfill(List<Tuple<Target, FullTask, int>> backfill) {
- if (backfill.length <= config.backfillerTargetLimit) {
- return backfill;
- }
- final List<Tuple<Target, FullTask, int>> filteredBackfill = <Tuple<Target, FullTask, int>>[];
- final List<Tuple<Target, FullTask, int>> highPriorityBackfill =
- backfill.where((element) => element.third == LuciBuildService.kRerunPriority).toList();
- final List<Tuple<Target, FullTask, int>> normalPriorityBackfill =
- backfill.where((element) => element.third != LuciBuildService.kRerunPriority).toList();
- if (highPriorityBackfill.length >= config.backfillerTargetLimit) {
- highPriorityBackfill.shuffle();
- filteredBackfill.addAll(highPriorityBackfill.sublist(0, config.backfillerTargetLimit));
- } else {
- filteredBackfill.addAll(highPriorityBackfill);
- normalPriorityBackfill.shuffle();
- filteredBackfill
- .addAll(normalPriorityBackfill.sublist(0, config.backfillerTargetLimit - highPriorityBackfill.length));
- }
- return filteredBackfill;
- }
-
- /// Schedules tasks with retry when hitting pub/sub server errors.
- Future<void> _scheduleWithRetries(List<Tuple<Target, FullTask, int>> backfill) async {
- const RetryOptions retryOptions = Config.schedulerRetry;
- try {
- await retryOptions.retry(
- () async {
- final List<List<Tuple<Target, Task, int>>> tupleLists =
- await Future.wait<List<Tuple<Target, Task, int>>>(backfillRequestList(backfill));
- if (tupleLists.any((List<Tuple<Target, Task, int>> tupleList) => tupleList.isNotEmpty)) {
- final int nonEmptyListLenght = tupleLists.where((element) => element.isNotEmpty).toList().length;
- log.info('Backfill fails and retry backfilling $nonEmptyListLenght targets.');
- backfill = _updateBackfill(backfill, tupleLists);
- throw InternalServerError('Failed to backfill ${backfill.length} targets.');
- }
- },
- retryIf: (Exception e) => e is InternalServerError,
- );
- } catch (error) {
- log.severe('Failed to backfill ${backfill.length} targets due to error: $error');
- }
- }
-
- /// Updates the [backfill] list with those that fail to get scheduled.
- ///
- /// [tupleLists] maintains the same tuple order as those in [backfill].
- /// Each element from [backfill] is encapsulated as a list in [tupleLists] to prepare for
- /// [scheduler.luciBuildService.schedulePostsubmitBuilds].
- List<Tuple<Target, FullTask, int>> _updateBackfill(
- List<Tuple<Target, FullTask, int>> backfill,
- List<List<Tuple<Target, Task, int>>> tupleLists,
- ) {
- final List<Tuple<Target, FullTask, int>> updatedBackfill = <Tuple<Target, FullTask, int>>[];
- for (int i = 0; i < tupleLists.length; i++) {
- if (tupleLists[i].isNotEmpty) {
- updatedBackfill.add(backfill[i]);
- }
- }
- return updatedBackfill;
- }
-
- /// Creates a list of backfill requests.
- List<Future<List<Tuple<Target, Task, int>>>> backfillRequestList(List<Tuple<Target, FullTask, int>> backfill) {
- final List<Future<List<Tuple<Target, Task, int>>>> futures = <Future<List<Tuple<Target, Task, int>>>>[];
- for (Tuple<Target, FullTask, int> tuple in backfill) {
- // TODO(chillers): The backfill priority is always going to be low. If this is a ToT task, we should run it at the default priority.
- final Tuple<Target, Task, int> toBeScheduled = Tuple(
- tuple.first,
- tuple.second.task,
- tuple.third,
- );
- futures.add(
- scheduler.luciBuildService.schedulePostsubmitBuilds(
- commit: tuple.second.commit,
- toBeScheduled: [toBeScheduled],
- ),
- );
- }
-
- return futures;
- }
-
- /// Returns priority for back filled targets.
- ///
- /// Skips scheduling newly created targets whose available entries are
- /// less than `BatchPolicy.kBatchSize`.
- ///
- /// Uses a higher priority if there is an earlier failed build. Otherwise,
- /// uses default `LuciBuildService.kBackfillPriority`
- int? backfillPriority(List<Task> tasks) {
- if (tasks.length < BatchPolicy.kBatchSize) {
- return null;
- }
- if (shouldRerunPriority(tasks, BatchPolicy.kBatchSize)) {
- return LuciBuildService.kRerunPriority;
- }
- return LuciBuildService.kBackfillPriority;
- }
-
- /// Returns the most recent [FullTask] to backfill.
- ///
- /// A [FullTask] is only returned iff:
- /// 1. There are no running builds (yellow)
- /// 2. There are tasks that haven't been run (gray)
- ///
- /// This is naive, and doesn't rely on knowing the actual Flutter infra capacity.
- ///
- /// Otherwise, returns null indicating nothing should be backfilled.
- FullTask? _backfillTask(Target target, List<FullTask> tasks) {
- final List<FullTask> relevantTasks = tasks.where((FullTask task) => task.task.name == target.value.name).toList();
- if (relevantTasks.any((FullTask task) => task.task.status == Task.statusInProgress)) {
- // Don't schedule more builds where there is already a running task
- return null;
- }
-
- final List<FullTask> backfillTask =
- relevantTasks.where((FullTask task) => task.task.status == Task.statusNew).toList();
- if (backfillTask.isEmpty) {
- return null;
- }
-
- // First item in the list is guranteed to be most recent.
- // Mark task as in progress to ensure it isn't scheduled over
- backfillTask.first.task.status = Task.statusInProgress;
- return backfillTask.first;
- }
-}
diff --git a/app_dart/lib/src/request_handlers/scheduler/request_subscription.dart b/app_dart/lib/src/request_handlers/scheduler/request_subscription.dart
deleted file mode 100644
index 874d16d..0000000
--- a/app_dart/lib/src/request_handlers/scheduler/request_subscription.dart
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright 2019 The Flutter 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:convert';
-
-import 'package:meta/meta.dart';
-import 'package:retry/retry.dart';
-
-import '../../../cocoon_service.dart';
-import '../../model/luci/buildbucket.dart';
-import '../../request_handling/exceptions.dart';
-import '../../request_handling/subscription_handler.dart';
-import '../../service/logging.dart';
-
-/// Subscription for making requests to BuildBucket.
-///
-/// The PubSub subscription is set up here:
-/// https://console.cloud.google.com/cloudpubsub/subscription/detail/scheduler-requests?project=flutter-dashboard
-///
-/// This endpoint allows Cocoon to defer BuildBucket requests off the main request loop. This is critical when new
-/// commits are pushed, and they can schedule 100+ builds at once.
-///
-/// This endpoint takes in a POST request with the JSON of a [BatchRequest]. In practice, the
-/// [BatchRequest] should contain a single request.
-@immutable
-class SchedulerRequestSubscription extends SubscriptionHandler {
- /// Creates a subscription for sending BuildBucket requests.
- const SchedulerRequestSubscription({
- required super.cache,
- required super.config,
- required this.buildBucketClient,
- super.authProvider,
- this.retryOptions = Config.schedulerRetry,
- }) : super(subscriptionName: 'scheduler-requests');
-
- final BuildBucketClient buildBucketClient;
-
- final RetryOptions retryOptions;
-
- @override
- Future<Body> post() async {
- BatchRequest request;
- try {
- final String data = message.data!;
- Map<String, dynamic> jsonData;
- log.info('rawJson: $data');
- try {
- jsonData = jsonDecode(data) as Map<String, dynamic>;
- } on FormatException {
- jsonData = json.decode(String.fromCharCodes(base64.decode(data))) as Map<String, dynamic>;
- }
- request = BatchRequest.fromJson(jsonData);
- } catch (e) {
- log.severe('Failed to construct BatchRequest from message');
- log.severe(e);
- throw BadRequestException(e.toString());
- }
-
- /// Retry scheduling builds upto 3 times.
- ///
- /// Log error message when still failing after retry. Avoid endless rescheduling
- /// by acking the pub/sub message without throwing an exception.
- String? unScheduledBuilds;
- try {
- await retryOptions.retry(
- () async {
- final List<Request> requestsToRetry = await _sendBatchRequest(request);
- request = BatchRequest(requests: requestsToRetry);
- unScheduledBuilds = requestsToRetry.map((e) => e.scheduleBuild!.builderId.builder).toString();
- if (requestsToRetry.isNotEmpty) {
- throw InternalServerError('Failed to schedule builds: $unScheduledBuilds.');
- }
- },
- retryIf: (Exception e) => e is InternalServerError,
- );
- } catch (e) {
- log.warning('Failed to schedule builds: $unScheduledBuilds.');
- return Body.forString('Failed to schedule builds: $unScheduledBuilds.');
- }
-
- return Body.empty;
- }
-
- /// Wrapper around [BuildbucketClient.batch] to ensure all requests are made.
- ///
- /// Returns [List<Request>] of requests that need to be retried.
- Future<List<Request>> _sendBatchRequest(BatchRequest request) async {
- final BatchResponse response = await buildBucketClient.batch(request);
- log.fine('Made ${request.requests?.length} and received ${response.responses?.length}');
- log.fine('Responses: ${response.responses}');
-
- // By default, retry everything. Then remove requests with a verified response.
- final List<Request> retry = request.requests ?? <Request>[];
- response.responses?.forEach((Response subresponse) {
- if (subresponse.scheduleBuild != null) {
- retry
- .removeWhere((Request request) => request.scheduleBuild?.builderId == subresponse.scheduleBuild!.builderId);
- } else {
- log.warning('Response does not have schedule build: $subresponse');
- }
- if (subresponse.error?.code != 0) {
- log.fine('Non-zero grpc code: $subresponse');
- }
- });
-
- return retry;
- }
-}
diff --git a/app_dart/lib/src/request_handlers/scheduler/vacuum_stale_tasks.dart b/app_dart/lib/src/request_handlers/scheduler/vacuum_stale_tasks.dart
deleted file mode 100644
index 852c526..0000000
--- a/app_dart/lib/src/request_handlers/scheduler/vacuum_stale_tasks.dart
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-
-import 'package:cocoon_service/cocoon_service.dart';
-import 'package:github/github.dart' as gh;
-import 'package:meta/meta.dart';
-
-import '../../model/appengine/task.dart';
-import '../../service/datastore.dart';
-import '../../service/logging.dart';
-
-/// Vacuum stale tasks.
-///
-/// Occassionally, a build never gets processed by LUCI. To prevent tasks
-/// being stuck as "In Progress," this will return tasks to "New" if they have
-/// no updates after 3 hours.
-@immutable
-class VacuumStaleTasks extends RequestHandler<Body> {
- const VacuumStaleTasks({
- required super.config,
- @visibleForTesting this.datastoreProvider = DatastoreService.defaultProvider,
- @visibleForTesting this.nowValue,
- });
-
- final DatastoreServiceProvider datastoreProvider;
-
- /// For testing, can be used to inject a deterministic time.
- final DateTime? nowValue;
-
- /// Tasks that are in progress for this duration will be reset.
- static const Duration kTimeoutLimit = Duration(hours: 3);
-
- @override
- Future<Body> get() async {
- final List<Future<void>> futures = <Future<void>>[];
- for (gh.RepositorySlug slug in config.supportedRepos) {
- futures.add(_vacuumRepository(slug));
- }
-
- await Future.wait(futures);
-
- return Body.empty;
- }
-
- /// Scans [slug] for tasks that have reached the timeout limit, and sets
- /// them back to the new state.
- ///
- /// The expectation is the [BatchBackfiller] will be able to reschedule these.
- Future<void> _vacuumRepository(gh.RepositorySlug slug) async {
- final DatastoreService datastore = datastoreProvider(config.db);
-
- final List<FullTask> tasks = await datastore.queryRecentTasks(slug: slug).toList();
- final Set<Task> tasksToBeReset = <Task>{};
- for (FullTask fullTask in tasks) {
- final Task task = fullTask.task;
- if (task.status != Task.statusInProgress) {
- continue;
- }
-
- if (task.createTimestamp == null) {
- log.fine('Vacuuming $task due to createTimestamp being null');
- tasksToBeReset.add(task);
- continue;
- }
-
- final DateTime now = nowValue ?? DateTime.now();
- final DateTime create = DateTime.fromMillisecondsSinceEpoch(task.createTimestamp!);
- final Duration queueTime = now.difference(create);
-
- if (queueTime > kTimeoutLimit) {
- log.fine('Vacuuming $task due to staleness');
- tasksToBeReset.add(task);
- continue;
- }
- }
-
- final Iterable<Task> inserts =
- tasksToBeReset.map((Task task) => task..status = Task.statusNew).map((Task task) => task..createTimestamp = 0);
- await datastore.insert(inserts.toList());
- }
-}
diff --git a/app_dart/lib/src/request_handlers/test_ownership.dart b/app_dart/lib/src/request_handlers/test_ownership.dart
deleted file mode 100644
index cdf3210..0000000
--- a/app_dart/lib/src/request_handlers/test_ownership.dart
+++ /dev/null
@@ -1,210 +0,0 @@
-// Copyright 2023 The Flutter 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 'package:cocoon_service/src/request_handlers/flaky_handler_utils.dart';
-import '../../protos.dart' as pb;
-
-abstract class TestOwner {
- factory TestOwner(BuilderType builderType) {
- switch (builderType) {
- case BuilderType.devicelab:
- return DeviceLabTestOwner();
- case BuilderType.firebaselab:
- return FirebaseLabTestOwner();
- case BuilderType.frameworkHostOnly:
- return FrameworkHostOnlyTestOwner();
- case BuilderType.shard:
- return ShardTestOwner();
- default:
- return UnknownTestOwner();
- }
- }
-
- TestOwnership getTestOwnership(
- pb.Target target,
- String testOwnersContent,
- );
-}
-
-Team teamFromString(String teamString) {
- switch (teamString) {
- case 'flutter/framework':
- return Team.framework;
- case 'flutter/engine':
- return Team.engine;
- case 'flutter/tool':
- return Team.tool;
- case 'flutter/web':
- return Team.web;
- }
- return Team.unknown;
-}
-
-String getTestNameFromTargetName(String targetName) {
- // The builder names is in the format '<platform> <test name>'.
- final List<String> words = targetName.split(' ');
- return words.length < 2 ? words[0] : words[1];
-}
-
-class DeviceLabTestOwner implements TestOwner {
- DeviceLabTestOwner();
-
- @override
- TestOwnership getTestOwnership(
- pb.Target target,
- String testOwnersContent,
- ) {
- String? owner;
- Team? team;
- final String testName = target.properties['task_name']!;
- // The format looks like this:
- // /dev/devicelab/bin/tasks/dart_plugin_registry_test.dart @stuartmorgan @flutter/plugin
- final RegExpMatch? match = devicelabTestOwners.firstMatch(testOwnersContent);
- if (match != null && match.namedGroup(kOwnerGroupName) != null) {
- final List<String> lines = match
- .namedGroup(kOwnerGroupName)!
- .split('\n')
- .where((String line) => line.isNotEmpty && !line.startsWith('#'))
- .toList();
-
- for (final String line in lines) {
- final List<String> words = line.trim().split(' ');
- // e.g. words = ['/xxx/xxx/xxx_test.dart', '@stuartmorgan' '@flutter/tool']
- if (words[0].endsWith('$testName.dart')) {
- owner = words[1].substring(1); // Strip out the lead '@'
- team = words.length < 3 ? Team.unknown : teamFromString(words[2].substring(1)); // Strip out the lead '@'
- break;
- }
- }
- }
-
- return TestOwnership(owner, team);
- }
-}
-
-class ShardTestOwner implements TestOwner {
- @override
- TestOwnership getTestOwnership(
- pb.Target target,
- String testOwnersContent,
- ) {
- // The format looks like this:
- // # build_tests @zanderso @flutter/tool
- final String testName = getTestNameFromTargetName(target.name);
- String? owner;
- Team? team;
- final RegExpMatch? match = shardTestOwners.firstMatch(testOwnersContent);
- if (match != null && match.namedGroup(kOwnerGroupName) != null) {
- final List<String> lines =
- match.namedGroup(kOwnerGroupName)!.split('\n').where((String line) => line.contains('@')).toList();
-
- for (final String line in lines) {
- final List<String> words = line.trim().split(' ');
- // e.g. words = ['#', 'build_test', '@zanderso' '@flutter/tool']
- if (testName.contains(words[1])) {
- owner = words[2].substring(1); // Strip out the lead '@'
- team = words.length < 4 ? Team.unknown : teamFromString(words[3].substring(1)); // Strip out the lead '@'
- break;
- }
- }
- }
-
- return TestOwnership(owner, team);
- }
-}
-
-class FrameworkHostOnlyTestOwner implements TestOwner {
- @override
- TestOwnership getTestOwnership(
- pb.Target target,
- String testOwnersContent,
- ) {
- final String testName = getTestNameFromTargetName(target.name);
- String? owner;
- Team? team;
- // The format looks like this:
- // # Linux analyze
- // /dev/bots/analyze.dart @HansMuller @flutter/framework
- final RegExpMatch? match = frameworkHostOnlyTestOwners.firstMatch(testOwnersContent);
- if (match != null && match.namedGroup(kOwnerGroupName) != null) {
- final List<String> lines =
- match.namedGroup(kOwnerGroupName)!.split('\n').where((String line) => line.isNotEmpty).toList();
- int index = 0;
- while (index < lines.length) {
- if (lines[index].startsWith('#')) {
- // Multiple tests can share same test file and ownership.
- // e.g.
- // # Linux docs_test
- // # Linux docs_public
- // /dev/bots/docs.sh @HansMuller @flutter/framework
- bool isTestDefined = false;
- while (lines[index].startsWith('#') && index + 1 < lines.length) {
- final List<String> commentWords = lines[index].trim().split(' ');
- if (testName.contains(commentWords[2])) {
- isTestDefined = true;
- }
- index += 1;
- }
- if (isTestDefined) {
- final List<String> ownerWords = lines[index].trim().split(' ');
- // e.g. ownerWords = ['/xxx/xxx/xxx_test.dart', '@HansMuller' '@flutter/framework']
- owner = ownerWords[1].substring(1); // Strip out the lead '@'
- team = ownerWords.length < 3
- ? Team.unknown
- : teamFromString(ownerWords[2].substring(1)); // Strip out the lead '@'
- break;
- }
- }
- index += 1;
- }
- }
-
- return TestOwnership(owner, team);
- }
-}
-
-class FirebaseLabTestOwner implements TestOwner {
- @override
- TestOwnership getTestOwnership(
- pb.Target target,
- String testOwnersContent,
- ) {
- final String testName = getTestNameFromTargetName(target.name);
- String? owner;
- Team? team;
-
- // The format looks like this for builder `Linux firebase_abstrac_method_smoke_test`:
- // /dev/integration_tests/abstrac_method_smoke_test @blasten @flutter/android
- final RegExpMatch? match = firebaselabTestOwners.firstMatch(testOwnersContent);
- if (match != null && match.namedGroup(kOwnerGroupName) != null) {
- final List<String> lines = match
- .namedGroup(kOwnerGroupName)!
- .split('\n')
- .where((String line) => line.isNotEmpty && !line.startsWith('#'))
- .toList();
-
- for (final String line in lines) {
- final List<String> words = line.trim().split(' ');
- final List<String> dirs = words[0].split('/').toList();
- if (testName.contains(dirs.last)) {
- owner = words[1].substring(1); // Strip out the lead '@'
- team = words.length < 3 ? Team.unknown : teamFromString(words[2].substring(1)); // Strip out the lead '@'
- break;
- }
- }
- }
-
- return TestOwnership(owner, team);
- }
-}
-
-class UnknownTestOwner implements TestOwner {
- @override
- TestOwnership getTestOwnership(
- pb.Target target,
- String testOwnersContent,
- ) {
- return TestOwnership(null, Team.unknown);
- }
-}
diff --git a/app_dart/lib/src/request_handlers/update_existing_flaky_issues.dart b/app_dart/lib/src/request_handlers/update_existing_flaky_issues.dart
deleted file mode 100644
index d1f2b58..0000000
--- a/app_dart/lib/src/request_handlers/update_existing_flaky_issues.dart
+++ /dev/null
@@ -1,176 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-
-import 'package:cocoon_service/ci_yaml.dart';
-import 'package:github/github.dart';
-import 'package:meta/meta.dart';
-import 'package:yaml/yaml.dart';
-
-import '../../protos.dart' as pb;
-import '../foundation/utils.dart';
-import '../request_handling/api_request_handler.dart';
-import '../request_handling/body.dart';
-import '../service/bigquery.dart';
-import '../service/config.dart';
-import '../service/github_service.dart';
-import 'flaky_handler_utils.dart';
-
-/// This handler updates existing open flaky issues with the latest build
-/// statistics.
-///
-/// The query parameter kThresholdKey is required in order for the handler to
-/// properly adjusts the priority labels.
-@immutable
-class UpdateExistingFlakyIssue extends ApiRequestHandler<Body> {
- const UpdateExistingFlakyIssue({
- required super.config,
- required super.authenticationProvider,
- @visibleForTesting this.ciYaml,
- });
-
- static const String kThresholdKey = 'threshold';
- static const int kFreshPeriodForOpenFlake = 7; // days
-
- final CiYaml? ciYaml;
-
- @override
- Future<Body> get() async {
- final RepositorySlug slug = Config.flutterSlug;
- final GithubService gitHub = config.createGithubServiceWithToken(await config.githubOAuthToken);
- final BigqueryService bigquery = await config.createBigQueryService();
-
- CiYaml? localCiYaml = ciYaml;
- if (localCiYaml == null) {
- final YamlMap? ci = loadYaml(
- await gitHub.getFileContent(
- slug,
- kCiYamlPath,
- ),
- ) as YamlMap?;
- final pb.SchedulerConfig unCheckedSchedulerConfig = pb.SchedulerConfig()..mergeFromProto3Json(ci);
- localCiYaml = CiYaml(
- slug: slug,
- branch: Config.defaultBranch(slug),
- config: unCheckedSchedulerConfig,
- );
- }
-
- final List<BuilderStatistic> prodBuilderStatisticList =
- await bigquery.listBuilderStatistic(kBigQueryProjectId, bucket: 'prod');
- final List<BuilderStatistic> stagingBuilderStatisticList =
- await bigquery.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging');
- final Map<String?, Issue> nameToExistingIssue = await getExistingIssues(gitHub, slug, state: 'open');
- await _updateExistingFlakyIssue(
- gitHub,
- slug,
- localCiYaml,
- prodBuilderStatisticList: prodBuilderStatisticList,
- stagingBuilderStatisticList: stagingBuilderStatisticList,
- nameToExistingIssue: nameToExistingIssue,
- );
- return Body.forJson(const <String, dynamic>{
- 'Status': 'success',
- });
- }
-
- double get _threshold => double.parse(request!.uri.queryParameters[kThresholdKey]!);
-
- /// Adds an update comment and adjusts the labels of the existing issue based
- /// on the latest statistics.
- ///
- /// This method skips issues that are created within kFreshPeriodForOpenFlake
- /// days.
- Future<void> _addCommentToExistingIssue(
- GithubService gitHub,
- RepositorySlug slug, {
- required Bucket bucket,
- required BuilderStatistic statistic,
- required Issue existingIssue,
- required CiYaml ciYaml,
- }) async {
- if (DateTime.now().difference(existingIssue.createdAt!) < const Duration(days: kFreshPeriodForOpenFlake)) {
- return;
- }
- final IssueUpdateBuilder updateBuilder =
- IssueUpdateBuilder(statistic: statistic, threshold: _threshold, existingIssue: existingIssue, bucket: bucket);
- await gitHub.createComment(slug, issueNumber: existingIssue.number, body: updateBuilder.issueUpdateComment);
- await gitHub.replaceLabelsForIssue(slug, issueNumber: existingIssue.number, labels: updateBuilder.issueLabels);
- if (existingIssue.assignee == null && !updateBuilder.isBelow) {
- final String testOwnerContent = await gitHub.getFileContent(
- slug,
- kTestOwnerPath,
- );
-
- final pb.SchedulerConfig schedulerConfig = ciYaml.config;
- final List<pb.Target> targets = schedulerConfig.targets;
-
- final String? testOwner = getTestOwnership(
- targets.singleWhere((element) => element.name == statistic.name),
- getTypeForBuilder(statistic.name, ciYaml),
- testOwnerContent,
- ).owner;
- if (testOwner != null) {
- await gitHub.assignIssue(slug, issueNumber: existingIssue.number, assignee: testOwner);
- }
- }
- }
-
- /// Updates existing flaky issues based on corrresponding builder stats.
- Future<void> _updateExistingFlakyIssue(
- GithubService gitHub,
- RepositorySlug slug,
- CiYaml ciYaml, {
- required List<BuilderStatistic> prodBuilderStatisticList,
- required List<BuilderStatistic> stagingBuilderStatisticList,
- required Map<String?, Issue> nameToExistingIssue,
- }) async {
- final Map<String, bool> builderFlakyMap = <String, bool>{};
- final Map<String, bool> ignoreFlakyMap = <String, bool>{};
- for (Target target in ciYaml.postsubmitTargets) {
- builderFlakyMap[target.value.name] = target.value.bringup;
- if (target.getIgnoreFlakiness()) {
- ignoreFlakyMap[target.value.name] = true;
- }
- }
- // Update an existing flaky bug with only prod stats if the builder is with `bringup: false`, such as a shard builder.
- //
- // Update an existing flaky bug with both prod and staging stats if the builder is with `bringup: true`. When a builder
- // is newly identified as flaky, there is a gap between the builder is marked as `bringup: true` and the flaky bug is filed.
- // For this case, there will be builds still running in `prod` pool, and we need to append `prod` stats as well.
- for (final BuilderStatistic statistic in prodBuilderStatisticList) {
- // ignore: iterable_contains_unrelated_type
- if (nameToExistingIssue.containsKey(statistic.name) &&
- builderFlakyMap.containsKey(statistic.name) &&
- // ignore: iterable_contains_unrelated_type
- !ignoreFlakyMap.containsKey(statistic.name)) {
- await _addCommentToExistingIssue(
- gitHub,
- slug,
- bucket: Bucket.prod,
- statistic: statistic,
- existingIssue: nameToExistingIssue[statistic.name]!,
- ciYaml: ciYaml,
- );
- }
- }
- // For all staging builder stats, updates any existing flaky bug.
- for (final BuilderStatistic statistic in stagingBuilderStatisticList) {
- if (nameToExistingIssue.containsKey(statistic.name) &&
- builderFlakyMap[statistic.name] == true &&
- // ignore: iterable_contains_unrelated_type
- !ignoreFlakyMap.containsKey(statistic.name)) {
- await _addCommentToExistingIssue(
- gitHub,
- slug,
- bucket: Bucket.staging,
- statistic: statistic,
- existingIssue: nameToExistingIssue[statistic.name]!,
- ciYaml: ciYaml,
- );
- }
- }
- }
-}
diff --git a/app_dart/lib/src/request_handlers/update_task_status.dart b/app_dart/lib/src/request_handlers/update_task_status.dart
deleted file mode 100644
index e4e6ed4..0000000
--- a/app_dart/lib/src/request_handlers/update_task_status.dart
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-
-import 'package:gcloud/db.dart';
-import 'package:meta/meta.dart';
-
-import '../model/appengine/commit.dart';
-import '../model/appengine/task.dart';
-import '../request_handling/api_request_handler.dart';
-import '../request_handling/body.dart';
-import '../request_handling/exceptions.dart';
-import '../service/datastore.dart';
-import '../service/logging.dart';
-
-/// Endpoint for task runners to update Cocoon with test run information.
-///
-/// This handler requires (1) task identifier and (2) task status information.
-///
-/// 1. Tasks are identified by:
-/// [gitBranchParam], [gitShaParam], [builderNameParam]
-///
-/// 2. Task status information
-/// A. Required: [newStatusParam], either [Task.statusSucceeded] or [Task.statusFailed].
-/// B. Optional: [resultsParam] and [scoreKeysParam] which hold performance benchmark data.
-@immutable
-class UpdateTaskStatus extends ApiRequestHandler<UpdateTaskStatusResponse> {
- const UpdateTaskStatus({
- required super.config,
- required super.authenticationProvider,
- @visibleForTesting this.datastoreProvider = DatastoreService.defaultProvider,
- });
-
- final DatastoreServiceProvider datastoreProvider;
-
- static const String gitBranchParam = 'CommitBranch';
- static const String gitShaParam = 'CommitSha';
- static const String newStatusParam = 'NewStatus';
- static const String builderNameParam = 'BuilderName';
- static const String testFlayParam = 'TestFlaky';
-
- @override
- Future<UpdateTaskStatusResponse> post() async {
- checkRequiredParameters(<String>[newStatusParam, gitBranchParam, gitShaParam, builderNameParam]);
-
- final DatastoreService datastore = datastoreProvider(config.db);
- final String newStatus = requestData![newStatusParam] as String;
- final bool isTestFlaky = (requestData![testFlayParam] as bool?) ?? false;
-
- if (newStatus != Task.statusSucceeded && newStatus != Task.statusFailed) {
- throw const BadRequestException('NewStatus can be one of "Succeeded", "Failed"');
- }
-
- final Task task = await _getTaskFromNamedParams(datastore);
-
- task.status = newStatus;
- task.endTimestamp = DateTime.now().millisecondsSinceEpoch;
- task.isTestFlaky = isTestFlaky;
-
- await datastore.insert(<Task>[task]);
- return UpdateTaskStatusResponse(task);
- }
-
- /// Retrieve [Task] from [DatastoreService] when given [gitShaParam], [gitBranchParam], and [builderNameParam].
- ///
- /// This is used when the DeviceLab test runner is uploading results to Cocoon for runs on LUCI.
- /// LUCI does not know the [Key] assigned to task when scheduling the build, but Cocoon can
- /// lookup the task based on these key values.
- ///
- /// To lookup the value, we construct the ancestor key, which corresponds to the [Commit].
- /// Then we query the tasks with that ancestor key and search for the one that matches the builder name.
- Future<Task> _getTaskFromNamedParams(DatastoreService datastore) async {
- final Key<String> commitKey = await _constructCommitKey(datastore);
-
- final String? builderName = requestData![builderNameParam] as String?;
- final Query<Task> query = datastore.db.query<Task>(ancestorKey: commitKey);
- final List<Task> initialTasks = await query.run().toList();
- log.fine('Found ${initialTasks.length} tasks for commit');
- final List<Task> tasks = <Task>[];
- log.fine('Searching for task with builderName=$builderName');
- for (Task task in initialTasks) {
- if (task.builderName == builderName || task.name == builderName) {
- tasks.add(task);
- }
- }
-
- if (tasks.length != 1) {
- log.severe('Found ${tasks.length} entries for builder $builderName');
- throw InternalServerError('Expected to find 1 task for $builderName, but found ${tasks.length}');
- }
-
- return tasks.first;
- }
-
- /// Construct the Datastore key for [Commit] that is the ancestor to this [Task].
- ///
- /// Throws [BadRequestException] if the given git branch does not exist in [CocoonConfig].
- Future<Key<String>> _constructCommitKey(DatastoreService datastore) async {
- final String gitBranch = (requestData![gitBranchParam] as String).trim();
- final String gitSha = (requestData![gitShaParam] as String).trim();
-
- final String id = 'flutter/flutter/$gitBranch/$gitSha';
- final Key<String> commitKey = datastore.db.emptyKey.append<String>(Commit, id: id);
- log.fine('Constructed commit key=$id');
- // Return the official key from Datastore for task lookups.
- final Commit commit = await config.db.lookupValue<Commit>(commitKey);
- return commit.key;
- }
-}
-
-@immutable
-class UpdateTaskStatusResponse extends JsonBody {
- const UpdateTaskStatusResponse(this.task);
-
- final Task task;
-
- @override
- Map<String, dynamic> toJson() {
- return <String, dynamic>{
- 'Name': task.name,
- 'Status': task.status,
- };
- }
-}
diff --git a/app_dart/lib/src/request_handlers/vacuum_github_commits.dart b/app_dart/lib/src/request_handlers/vacuum_github_commits.dart
deleted file mode 100644
index 2f70d0c..0000000
--- a/app_dart/lib/src/request_handlers/vacuum_github_commits.dart
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-
-import 'package:gcloud/db.dart';
-import 'package:github/github.dart' as gh;
-import 'package:meta/meta.dart';
-import 'package:truncate/truncate.dart';
-
-import '../model/appengine/commit.dart';
-import '../request_handling/api_request_handler.dart';
-import '../request_handling/body.dart';
-import '../service/config.dart';
-import '../service/datastore.dart';
-import '../service/github_service.dart';
-import '../service/logging.dart';
-import '../service/scheduler.dart';
-
-/// Query GitHub for commits from the past day and ensure they exist in datastore.
-@immutable
-class VacuumGithubCommits extends ApiRequestHandler<Body> {
- const VacuumGithubCommits({
- required super.config,
- required super.authenticationProvider,
- required this.scheduler,
- @visibleForTesting this.datastoreProvider = DatastoreService.defaultProvider,
- });
-
- final DatastoreServiceProvider datastoreProvider;
-
- final Scheduler scheduler;
-
- static const String branchParam = 'branch';
-
- @override
- Future<Body> get() async {
- final DatastoreService datastore = datastoreProvider(config.db);
-
- for (gh.RepositorySlug slug in config.supportedRepos) {
- final String branch = request!.uri.queryParameters[branchParam] ?? Config.defaultBranch(slug);
- await _vacuumRepository(slug, datastore: datastore, branch: branch);
- }
-
- return Body.empty;
- }
-
- Future<void> _vacuumRepository(
- gh.RepositorySlug slug, {
- DatastoreService? datastore,
- required String branch,
- }) async {
- final GithubService githubService = await config.createGithubService(slug);
- final List<Commit> commits = await _vacuumBranch(
- slug,
- branch,
- datastore: datastore,
- githubService: githubService,
- );
- await scheduler.addCommits(commits);
- }
-
- Future<List<Commit>> _vacuumBranch(
- gh.RepositorySlug slug,
- String branch, {
- DatastoreService? datastore,
- required GithubService githubService,
- }) async {
- List<gh.RepositoryCommit> commits = <gh.RepositoryCommit>[];
- // Sliding window of times to add commits from.
- final DateTime queryAfter = DateTime.now().subtract(const Duration(days: 1));
- final DateTime queryBefore = DateTime.now().subtract(const Duration(minutes: 3));
- try {
- log.fine('Listing commit for slug: $slug branch: $branch and msSinceEpoch: ${queryAfter.millisecondsSinceEpoch}');
- commits = await githubService.listBranchedCommits(slug, branch, queryAfter.millisecondsSinceEpoch);
- log.fine('Retrieved ${commits.length} commits from GitHub');
- // Do not try to add recent commits as they may already be processed
- // by cocoon, which can cause race conditions.
- commits = commits
- .where(
- (gh.RepositoryCommit commit) =>
- commit.commit!.committer!.date!.millisecondsSinceEpoch < queryBefore.millisecondsSinceEpoch,
- )
- .toList();
- } on gh.GitHubError catch (error) {
- log.severe('$error');
- }
-
- return _toDatastoreCommit(slug, commits, datastore, branch);
- }
-
- /// Convert [gh.RepositoryCommit] to Cocoon's [Commit] format.
- Future<List<Commit>> _toDatastoreCommit(
- gh.RepositorySlug slug,
- List<gh.RepositoryCommit> commits,
- DatastoreService? datastore,
- String branch,
- ) async {
- final List<Commit> recentCommits = <Commit>[];
- for (gh.RepositoryCommit commit in commits) {
- final String id = '${slug.fullName}/$branch/${commit.sha}';
- final Key<String> key = datastore!.db.emptyKey.append<String>(Commit, id: id);
- recentCommits.add(
- Commit(
- key: key,
- timestamp: commit.commit!.committer!.date!.millisecondsSinceEpoch,
- repository: slug.fullName,
- sha: commit.sha!,
- author: commit.author!.login!,
- authorAvatarUrl: commit.author!.avatarUrl!,
- // The field has a size of 1500 we need to ensure the commit message
- // is at most 1500 chars long.
- message: truncate(commit.commit!.message!, 1490, omission: '...'),
- branch: branch,
- ),
- );
- }
- return recentCommits;
- }
-}
diff --git a/app_dart/lib/src/request_handling/api_request_handler.dart b/app_dart/lib/src/request_handling/api_request_handler.dart
deleted file mode 100644
index b20e178..0000000
--- a/app_dart/lib/src/request_handling/api_request_handler.dart
+++ /dev/null
@@ -1,168 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-import 'dart:convert';
-import 'dart:io';
-import 'dart:typed_data';
-
-import 'package:meta/meta.dart';
-
-import '../model/google/token_info.dart';
-import 'authentication.dart';
-import 'body.dart';
-import 'exceptions.dart';
-import 'request_handler.dart';
-
-/// A [RequestHandler] that handles API requests.
-///
-/// API requests adhere to a specific contract, as follows:
-///
-/// * All requests must be authenticated per [AuthenticationProvider].
-///
-/// `T` is the type of object that is returned as the body of the HTTP response
-/// (before serialization). Subclasses whose HTTP responses don't include a
-/// body should extend `RequestHandler<Body>` and return null in their service
-/// handlers ([get] and [post]).
-@immutable
-abstract class ApiRequestHandler<T extends Body> extends RequestHandler<T> {
- /// Creates a new [ApiRequestHandler].
- const ApiRequestHandler({
- required super.config,
- required this.authenticationProvider,
- this.requestBodyValue,
- });
-
- /// Service responsible for authenticating this [HttpRequest].
- final AuthenticationProvider authenticationProvider;
-
- /// Throws a [BadRequestException] if any of [requiredParameters] is missing
- /// from [requestData].
- @protected
- void checkRequiredParameters(List<String> requiredParameters) {
- final Iterable<String> missingParams = requiredParameters..removeWhere(requestData!.containsKey);
- if (missingParams.isNotEmpty) {
- throw BadRequestException('Missing required parameter: ${missingParams.join(', ')}');
- }
- }
-
- /// Gets [TokenInfo] using X-Flutter-IdToken header from an authenticated request.
- @protected
- Future<TokenInfo> tokenInfo(HttpRequest request) async {
- return authenticationProvider.tokenInfo(request);
- }
-
- /// Throws a [BadRequestException] if any of [requiredQueryParameters] are missing from [requestData].
- @protected
- void checkRequiredQueryParameters(List<String> requiredQueryParameters) {
- final Iterable<String> missingParams = requiredQueryParameters
- ..removeWhere(request!.uri.queryParameters.containsKey);
- if (missingParams.isNotEmpty) {
- throw BadRequestException('Missing required parameter: ${missingParams.join(', ')}');
- }
- }
-
- /// The authentication context associated with the HTTP request.
- ///
- /// This is guaranteed to be non-null. If the request was unauthenticated,
- /// the request will be denied.
- @protected
- AuthenticatedContext? get authContext => getValue<AuthenticatedContext>(ApiKey.authContext);
-
- /// The raw byte contents of the HTTP request body.
- ///
- /// If the request did not specify any content in the body, this will be an
- /// empty list. It will never be null.
- ///
- /// See also:
- ///
- /// * [requestData], which contains the JSON-decoded [Map] of the request
- /// body content (if applicable).
- @protected
- Uint8List? get requestBody => requestBodyValue ?? getValue<Uint8List>(ApiKey.requestBody);
-
- /// Used for injecting [requestBody] in tests.
- final Uint8List? requestBodyValue;
-
- /// The JSON data specified in the HTTP request body.
- ///
- /// This is guaranteed to be non-null. If the request body was empty, or if
- /// it contained non-JSON or binary (non-UTF-8) data, this will be an empty
- /// map.
- ///
- /// See also:
- ///
- /// * [requestBody], which specifies the raw bytes of the HTTP request body.
- @protected
- Map<String, dynamic>? get requestData => getValue<Map<String, dynamic>>(ApiKey.requestData);
-
- @override
- Future<void> service(
- HttpRequest request, {
- Future<void> Function(HttpStatusException)? onError,
- }) async {
- AuthenticatedContext context;
- try {
- context = await authenticationProvider.authenticate(request);
- } on Unauthenticated catch (error) {
- final HttpResponse response = request.response;
- response
- ..statusCode = HttpStatus.unauthorized
- ..write(error.message);
- await response.flush();
- await response.close();
- return;
- }
-
- List<int> body;
- try {
- body = await request.expand<int>((List<int> chunk) => chunk).toList();
- } catch (error) {
- final HttpResponse response = request.response;
- response
- ..statusCode = HttpStatus.internalServerError
- ..write('$error');
- await response.flush();
- await response.close();
- return;
- }
-
- Map<String, dynamic>? requestData = const <String, dynamic>{};
- if (body.isNotEmpty) {
- try {
- requestData = json.decode(utf8.decode(body)) as Map<String, dynamic>?;
- } on FormatException {
- // The HTTP request body is not valid UTF-8 encoded JSON. This is
- // allowed; just let [requestData] be null.
- } catch (error) {
- final HttpResponse response = request.response;
- response
- ..statusCode = HttpStatus.internalServerError
- ..write('$error');
- await response.flush();
- await response.close();
- return;
- }
- }
-
- await runZoned<Future<void>>(
- () async {
- await super.service(request);
- },
- zoneValues: <ApiKey<dynamic>, Object?>{
- ApiKey.authContext: context,
- ApiKey.requestBody: Uint8List.fromList(body),
- ApiKey.requestData: requestData,
- },
- );
- }
-}
-
-class ApiKey<T> extends RequestKey<T> {
- const ApiKey._(super.name);
-
- static const ApiKey<Uint8List> requestBody = ApiKey<Uint8List>._('requestBody');
- static const ApiKey<AuthenticatedContext> authContext = ApiKey<AuthenticatedContext>._('authenticatedContext');
- static const ApiKey<Map<String, dynamic>> requestData = ApiKey<Map<String, dynamic>>._('requestData');
-}
diff --git a/app_dart/lib/src/request_handling/authentication.dart b/app_dart/lib/src/request_handling/authentication.dart
deleted file mode 100644
index 21d99ee..0000000
--- a/app_dart/lib/src/request_handling/authentication.dart
+++ /dev/null
@@ -1,178 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-import 'dart:convert';
-import 'dart:io';
-
-import 'package:appengine/appengine.dart';
-import 'package:gcloud/db.dart';
-import 'package:http/http.dart' as http;
-import 'package:meta/meta.dart';
-
-import '../../cocoon_service.dart';
-import '../foundation/providers.dart';
-import '../foundation/typedefs.dart';
-import '../model/appengine/allowed_account.dart';
-import '../model/google/token_info.dart';
-import '../service/logging.dart';
-import 'exceptions.dart';
-
-/// Class capable of authenticating [HttpRequest]s.
-///
-/// There are two types of authentication this class supports:
-///
-/// 1. If the request has the `'X-Appengine-Cron'` HTTP header set to "true",
-/// then the request will be authenticated as an App Engine cron job.
-///
-/// The `'X-Appengine-Cron'` HTTP header is set automatically by App Engine
-/// and will be automatically stripped from the request by the App Engine
-/// runtime if the request originated from anything other than a cron job.
-/// Thus, the header is safe to trust as an authentication indicator.
-///
-/// 2. If the request has the `'X-Flutter-IdToken'` HTTP header
-/// set to a valid encrypted JWT token, then the request will be authenticated
-/// as a user account.
-///
-/// @google.com accounts can call APIs using curl and gcloud.
-/// E.g. curl '<api_url>' -H "X-Flutter-IdToken: $(gcloud auth print-identity-token)"
-///
-/// User accounts are only authorized if the user is either a "@google.com"
-/// account or is an [AllowedAccount] in Cocoon's Datastore.
-///
-/// If none of the above authentication methods yield an authenticated
-/// request, then the request is unauthenticated, and any call to
-/// [authenticate] will throw an [Unauthenticated] exception.
-///
-/// See also:
-///
-/// * <https://cloud.google.com/appengine/docs/standard/python/reference/request-response-headers>
-@immutable
-class AuthenticationProvider {
- const AuthenticationProvider({
- required this.config,
- this.clientContextProvider = Providers.serviceScopeContext,
- this.httpClientProvider = Providers.freshHttpClient,
- });
-
- /// The Cocoon config, guaranteed to be non-null.
- final Config config;
-
- /// Provides the App Engine client context as part of the
- /// [AuthenticatedContext].
- ///
- /// This is guaranteed to be non-null.
- final ClientContextProvider clientContextProvider;
-
- /// Provides the HTTP client that will be used (if necessary) to verify OAuth
- /// ID tokens (JWT tokens).
- ///
- /// This is guaranteed to be non-null.
- final HttpClientProvider httpClientProvider;
-
- /// Authenticates the specified [request] and returns the associated
- /// [AuthenticatedContext].
- ///
- /// See the class documentation on [AuthenticationProvider] for a discussion
- /// of the different types of authentication that are accepted.
- ///
- /// This will throw an [Unauthenticated] exception if the request is
- /// unauthenticated.
- Future<AuthenticatedContext> authenticate(HttpRequest request) async {
- final bool isCron = request.headers.value('X-Appengine-Cron') == 'true';
- final String? idTokenFromHeader = request.headers.value('X-Flutter-IdToken');
- final ClientContext clientContext = clientContextProvider();
- if (isCron) {
- // Authenticate cron requests
- return AuthenticatedContext(clientContext: clientContext);
- } else if (idTokenFromHeader != null) {
- TokenInfo token;
- try {
- token = await tokenInfo(request);
- } on Unauthenticated {
- token = await tokenInfo(request, tokenType: 'access_token');
- }
- return authenticateToken(token, clientContext: clientContext);
- }
-
- throw const Unauthenticated('User is not signed in');
- }
-
- /// Gets oauth token information. This method requires the token to be stored in
- /// X-Flutter-IdToken header.
- Future<TokenInfo> tokenInfo(HttpRequest request, {String tokenType = 'id_token'}) async {
- final String? idTokenFromHeader = request.headers.value('X-Flutter-IdToken');
- final http.Client client = httpClientProvider();
- try {
- final http.Response verifyTokenResponse = await client.get(
- Uri.https(
- 'oauth2.googleapis.com',
- '/tokeninfo',
- <String, String?>{
- tokenType: idTokenFromHeader,
- },
- ),
- );
-
- if (verifyTokenResponse.statusCode != HttpStatus.ok) {
- /// Google Auth API returns a message in the response body explaining why
- /// the request failed. Such as "Invalid Token".
- log.fine('Token verification failed: ${verifyTokenResponse.statusCode}; ${verifyTokenResponse.body}');
- throw const Unauthenticated('Invalid ID token');
- }
-
- try {
- return TokenInfo.fromJson(json.decode(verifyTokenResponse.body) as Map<String, dynamic>);
- } on FormatException {
- throw InternalServerError('Invalid JSON: "${verifyTokenResponse.body}"');
- }
- } finally {
- client.close();
- }
- }
-
- Future<AuthenticatedContext> authenticateToken(TokenInfo token, {required ClientContext clientContext}) async {
- // Authenticate as a signed-in Google account via OAuth id token.
- final String clientId = await config.oauthClientId;
- if (token.audience != clientId && !token.email!.endsWith('@google.com')) {
- log.warning('Possible forged token: "${token.audience}" (expected "$clientId")');
- throw const Unauthenticated('Invalid ID token');
- }
-
- if (token.hostedDomain != 'google.com') {
- final bool isAllowed = await _isAllowed(token.email);
- if (!isAllowed) {
- throw Unauthenticated('${token.email} is not authorized to access the dashboard');
- }
- }
- return AuthenticatedContext(clientContext: clientContext);
- }
-
- Future<bool> _isAllowed(String? email) async {
- final Query<AllowedAccount> query = config.db.query<AllowedAccount>()
- ..filter('email =', email)
- ..limit(20);
-
- return !(await query.run().isEmpty);
- }
-}
-
-/// Class that represents an authenticated request having been made, and any
-/// attached metadata to that request.
-///
-/// See also:
-///
-/// * [AuthenticationProvider]
-@immutable
-class AuthenticatedContext {
- /// Creates a new [AuthenticatedContext].
- const AuthenticatedContext({
- required this.clientContext,
- });
-
- /// The App Engine [ClientContext] of the current request.
- ///
- /// This is guaranteed to be non-null.
- final ClientContext clientContext;
-}
diff --git a/app_dart/lib/src/request_handling/body.dart b/app_dart/lib/src/request_handling/body.dart
deleted file mode 100644
index dec66ce..0000000
--- a/app_dart/lib/src/request_handling/body.dart
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2019 The Flutter 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:convert';
-import 'dart:typed_data';
-
-import 'package:meta/meta.dart';
-
-/// Class that represents an HTTP response body before it has been serialized.
-@immutable
-abstract class Body {
- /// Creates a new [Body].
- const Body();
-
- /// Creates a [Body] that serializes the specified String [content].
- factory Body.forString(String content) => _StringBody(content);
-
- /// Creates a [Body] that passes through the already-serialized [stream].
- factory Body.forStream(Stream<Uint8List?> stream) => _StreamBody(stream);
-
- /// Creates a [Body] that serializes the specified JSON [value].
- ///
- /// The [value] argument may be any JSON tyope (any scalar value, any object
- /// that defines a `toJson()` method that returns a JSON type, or a [List] or
- /// [Map] of other JSON types).
- factory Body.forJson(dynamic value) => Body.forString(json.encode(value));
-
- /// Value indicating that the HTTP response body should be empty.
- static const Body empty = _EmptyBody();
-
- /// Serializes this response body to bytes.
- Stream<Uint8List?> serialize();
-}
-
-abstract class JsonBody extends Body {
- const JsonBody();
-
- @override
- Stream<Uint8List> serialize() {
- final Stream<String> raw = json.encoder.bind(Stream<Object>.fromIterable(<Object>[toJson()]));
- return utf8.encoder.bind(raw).cast<Uint8List>();
- }
-
- /// Serializes this response body to a JSON-primitive map.
- Map<String, dynamic> toJson();
-}
-
-class _EmptyBody extends Body {
- const _EmptyBody();
-
- @override
- Stream<Uint8List> serialize() => const Stream<Uint8List>.empty();
-}
-
-class _StringBody extends Body {
- const _StringBody(this.content);
-
- final String content;
-
- @override
- Stream<Uint8List> serialize() {
- return utf8.encoder.bind(Stream<String>.fromIterable(<String>[content])).cast<Uint8List>();
- }
-}
-
-class _StreamBody extends Body {
- const _StreamBody(this.stream);
-
- final Stream<Uint8List?> stream;
-
- @override
- Stream<Uint8List?> serialize() => stream;
-}
diff --git a/app_dart/lib/src/request_handling/cache_request_handler.dart b/app_dart/lib/src/request_handling/cache_request_handler.dart
deleted file mode 100644
index 878fadd..0000000
--- a/app_dart/lib/src/request_handling/cache_request_handler.dart
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-import 'dart:typed_data';
-
-import 'package:meta/meta.dart';
-
-import '../request_handling/request_handler.dart';
-import '../service/cache_service.dart';
-import 'body.dart';
-
-/// A [RequestHandler] for serving cached responses.
-///
-/// High traffic endpoints that have responses that do not change
-/// based on request are good for caching. Additionally, saves
-/// reading from Datastore which is expensive both timewise and monetarily.
-@immutable
-class CacheRequestHandler<T extends Body> extends RequestHandler<T> {
- /// Creates a new [CacheRequestHandler].
- const CacheRequestHandler({
- required this.delegate,
- required super.config,
- required this.cache,
- this.ttl = const Duration(minutes: 1),
- });
-
- /// [RequestHandler] to fallback on for cache misses.
- final RequestHandler<T> delegate;
-
- final CacheService cache;
-
- /// The time to live for the response stored in the cache.
- final Duration ttl;
-
- @visibleForTesting
- static const String responseSubcacheName = 'response';
-
- @visibleForTesting
- static const String flushCacheQueryParam = 'flushCache';
-
- /// Services a cached request.
- ///
- /// Given the query param [flushCacheQueryParam]=true, it will purge the
- /// response from the cache before getting it to set the cached response
- /// to the latest information.
- @override
- Future<T> get() async {
- final String responseKey = '${request!.uri.path}:${request!.uri.query}';
-
- if (request!.uri.queryParameters[flushCacheQueryParam] == 'true') {
- await cache.purge(responseSubcacheName, responseKey);
- }
-
- final Uint8List? cachedResponse = await cache.getOrCreateWithLocking(
- responseSubcacheName,
- responseKey,
- createFn: () => getBodyBytesFromDelegate(delegate),
- ttl: ttl,
- );
-
- return Body.forStream(Stream<Uint8List?>.value(cachedResponse)) as T;
- }
-
- /// Get a Uint8List that contains the bytes of the response from [delegate]
- /// so it can be stored in [cache].
- Future<Uint8List> getBodyBytesFromDelegate(RequestHandler<T> delegate) async {
- final Body body = await delegate.get();
-
- // Body only offers getting a Stream<Uint8List> since it just sends
- // the data out usually to a client. In this case, we want to store
- // the bytes in the cache which requires several conversions to get a
- // Uint8List that contains the bytes of the response.
- final List<int> rawBytes = await body.serialize().expand<int>((Uint8List? chunk) => chunk!).toList();
- return Uint8List.fromList(rawBytes);
- }
-}
diff --git a/app_dart/lib/src/request_handling/exceptions.dart b/app_dart/lib/src/request_handling/exceptions.dart
deleted file mode 100644
index e40c436..0000000
--- a/app_dart/lib/src/request_handling/exceptions.dart
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2019 The Flutter 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';
-
-/// An exception that may be thrown by a [RequestHandler] to trigger an error
-/// HTTP response.
-class HttpStatusException implements Exception {
- /// Creates a new [HttpStatusException].
- const HttpStatusException(this.statusCode, this.message);
-
- /// The HTTP status code to return to the issuer.
- final int statusCode;
-
- /// The message to show to the issuer to explain the error.
- final String message;
-
- @override
- String toString() => 'HTTP $statusCode: $message';
-}
-
-/// Exception that will trigger an HTTP 400 bad request.
-class BadRequestException extends HttpStatusException {
- const BadRequestException([String message = 'Bad request']) : super(HttpStatus.badRequest, message);
-}
-
-/// Exception that will trigger an HTTP 404 not found
-class NotFoundException extends HttpStatusException {
- const NotFoundException(String missing) : super(HttpStatus.notFound, 'Not found: $missing');
-}
-
-/// Exception that will trigger an HTTP 405 method not allowed.
-class MethodNotAllowed extends HttpStatusException {
- const MethodNotAllowed(String method) : super(HttpStatus.methodNotAllowed, 'Unsupported method: $method');
-}
-
-/// Exception that will trigger an HTTP 409 conflict.
-class ConflictException extends HttpStatusException {
- const ConflictException([String message = 'Request conflict with server state'])
- : super(HttpStatus.conflict, message);
-}
-
-/// Exception that will trigger an HTTP 500 internal server error.
-class InternalServerError extends HttpStatusException {
- const InternalServerError([String message = 'Internal server error'])
- : super(HttpStatus.internalServerError, message);
-}
-
-/// Exception that will trigger an HTTP 401 not authorized.
-class Unauthorized extends HttpStatusException {
- const Unauthorized([String message = 'Unauthorized']) : super(HttpStatus.unauthorized, message);
-}
-
-/// Exception that will trigger an HTTP 403 forbidden.
-class Forbidden extends HttpStatusException {
- const Forbidden([String message = 'Forbidden']) : super(HttpStatus.forbidden, message);
-}
-
-/// Exception thrown when attempting to authenticate a request that cannot be
-/// authenticated.
-class Unauthenticated implements Exception {
- const Unauthenticated(this.message);
-
- final String message;
-
- @override
- String toString() => 'Unauthenticated: $message';
-}
diff --git a/app_dart/lib/src/request_handling/no_auth_request_handler.dart b/app_dart/lib/src/request_handling/no_auth_request_handler.dart
deleted file mode 100644
index 92a5244..0000000
--- a/app_dart/lib/src/request_handling/no_auth_request_handler.dart
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-import 'dart:convert';
-import 'dart:io';
-import 'dart:typed_data';
-
-import 'package:meta/meta.dart';
-
-import 'body.dart';
-import 'exceptions.dart';
-import 'request_handler.dart';
-
-/// A [RequestHandler] that handles requests with no authentication.
-///
-/// No Auth requests enables [requestData]
-///
-/// `T` is the type of object that is returned as the body of the HTTP response
-/// (before serialization). Subclasses whose HTTP responses don't include a
-/// body should extend `RequestHandler<Body>` and return null in their service
-/// handlers ([get] and [post]).
-@immutable
-abstract class NoAuthRequestHandler<T extends Body> extends RequestHandler<T> {
- /// Creates a new [NoAuthRequestHandler].
- const NoAuthRequestHandler({
- required super.config,
- });
-
- /// Throws a [BadRequestException] if any of [requiredParameters] is missing
- /// from [requestData].
- @protected
- void checkRequiredParameters(List<String> requiredParameters) {
- final Iterable<String> missingParams = requiredParameters..removeWhere(requestData!.containsKey);
- if (missingParams.isNotEmpty) {
- throw BadRequestException('Missing required parameter: ${missingParams.join(', ')}');
- }
- }
-
- /// The raw byte contents of the HTTP request body.
- ///
- /// If the request did not specify any content in the body, this will be an
- /// empty list. It will never be null.
- ///
- /// See also:
- ///
- /// * [requestData], which contains the JSON-decoded [Map] of the request
- /// body content (if applicable).
- @protected
- Uint8List? get requestBody => getValue<Uint8List>(NoAuthKey.requestBody);
-
- /// The JSON data specified in the HTTP request body.
- ///
- /// This is guaranteed to be non-null. If the request body was empty, or if
- /// it contained non-JSON or binary (non-UTF-8) data, this will be an empty
- /// map.
- ///
- /// See also:
- ///
- /// * [requestBody], which specifies the raw bytes of the HTTP request body.
- @protected
- Map<String, dynamic>? get requestData => getValue<Map<String, dynamic>>(NoAuthKey.requestData);
-
- @override
- Future<void> service(
- HttpRequest request, {
- Future<void> Function(HttpStatusException)? onError,
- }) async {
- List<int> body;
- try {
- body = await request.expand<int>((List<int> chunk) => chunk).toList();
- } catch (error) {
- final HttpResponse response = request.response;
- response
- ..statusCode = HttpStatus.internalServerError
- ..write('$error');
- await response.flush();
- await response.close();
- return;
- }
-
- Map<String, dynamic>? requestData = const <String, dynamic>{};
- if (body.isNotEmpty) {
- try {
- requestData = json.decode(utf8.decode(body)) as Map<String, dynamic>?;
- } on FormatException {
- // The HTTP request body is not valid UTF-8 encoded JSON. This is
- // allowed; just let [requestData] be null.
- } catch (error) {
- final HttpResponse response = request.response;
- response
- ..statusCode = HttpStatus.internalServerError
- ..write('$error');
- await response.flush();
- await response.close();
- return;
- }
- }
-
- await runZoned<Future<void>>(
- () async {
- await super.service(request);
- },
- zoneValues: <NoAuthKey<dynamic>, Object?>{
- NoAuthKey.requestBody: Uint8List.fromList(body),
- NoAuthKey.requestData: requestData,
- },
- );
- }
-}
-
-@visibleForTesting
-class NoAuthKey<T> extends RequestKey<T> {
- const NoAuthKey._(super.name);
-
- static const NoAuthKey<Uint8List> requestBody = NoAuthKey<Uint8List>._('requestBody');
- static const NoAuthKey<Map<String, dynamic>> requestData = NoAuthKey<Map<String, dynamic>>._('requestData');
-}
diff --git a/app_dart/lib/src/request_handling/pubsub.dart b/app_dart/lib/src/request_handling/pubsub.dart
deleted file mode 100644
index a608c5e..0000000
--- a/app_dart/lib/src/request_handling/pubsub.dart
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2019 The Flutter 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:convert';
-
-import 'package:googleapis/pubsub/v1.dart';
-import 'package:googleapis_auth/auth_io.dart';
-import 'package:http/http.dart';
-
-import '../foundation/providers.dart';
-import '../foundation/typedefs.dart';
-import '../service/logging.dart';
-
-/// Service class for interacting with PubSub.
-class PubSub {
- const PubSub({
- this.httpClientProvider = Providers.freshHttpClient,
- });
-
- final HttpClientProvider httpClientProvider;
-
- Future<List<String>> publish(String topic, dynamic json) async {
- final Client httpClient = await clientViaApplicationDefaultCredentials(
- scopes: <String>[
- PubsubApi.pubsubScope,
- ],
- );
- final PubsubApi pubsubApi = PubsubApi(httpClient);
- final String messageData = jsonEncode(json);
- final List<int> messageBytes = utf8.encode(messageData);
- final String messageBase64 = base64Encode(messageBytes);
- final PublishRequest request = PublishRequest(
- messages: <PubsubMessage>[
- PubsubMessage(data: messageBase64),
- ],
- );
- final String fullTopicName = 'projects/flutter-dashboard/topics/$topic';
- final PublishResponse response = await pubsubApi.projects.topics.publish(request, fullTopicName);
- log.info('pubsub response messageId=${response.messageIds}');
- return response.messageIds!;
- }
-}
diff --git a/app_dart/lib/src/request_handling/pubsub_authentication.dart b/app_dart/lib/src/request_handling/pubsub_authentication.dart
deleted file mode 100644
index fc9f90b..0000000
--- a/app_dart/lib/src/request_handling/pubsub_authentication.dart
+++ /dev/null
@@ -1,80 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-import 'dart:io';
-
-import 'package:appengine/appengine.dart';
-import 'package:googleapis/oauth2/v2.dart';
-import 'package:http/http.dart';
-import 'package:meta/meta.dart';
-
-import '../../cocoon_service.dart';
-import '../foundation/providers.dart';
-import '../service/logging.dart';
-import 'exceptions.dart';
-
-/// Class capable of authenticating [HttpRequest]s for PubSub messages.
-///
-/// This class implements an ACL on a [RequestHandler] to ensure only automated
-/// systems can access the endpoints.
-///
-/// If the request has [HttpHeaders.authorizationHeader], the token
-/// will be authenticated as a LUCI bot. This token is validated against
-/// Google Auth APIs.
-///
-/// If there is no token, or it cannot be authenticated, [Unauthenticated] is thrown.
-@immutable
-class PubsubAuthenticationProvider extends AuthenticationProvider {
- const PubsubAuthenticationProvider({
- required super.config,
- super.clientContextProvider = Providers.serviceScopeContext,
- super.httpClientProvider = Providers.freshHttpClient,
- });
-
- static const String kBearerTokenPrefix = 'Bearer ';
-
- /// Authenticates the specified [request] and returns the associated
- /// [AuthenticatedContext].
- ///
- /// See the class documentation on [AuthenticationProvider] for a discussion
- /// of the different types of authentication that are accepted.
- ///
- /// This will throw an [Unauthenticated] exception if the request is
- /// unauthenticated.
- @override
- Future<AuthenticatedContext> authenticate(HttpRequest request) async {
- final String? idToken = request.headers.value(HttpHeaders.authorizationHeader);
-
- final ClientContext clientContext = clientContextProvider();
-
- log.fine('Authenticating as pubsub message');
- return authenticateIdToken(idToken, clientContext: clientContext);
- }
-
- /// Authenticate [idToken] against Google OAuth 2 API.
- Future<AuthenticatedContext> authenticateIdToken(
- String? idToken, {
- required ClientContext clientContext,
- }) async {
- if (idToken == null || !idToken.startsWith(kBearerTokenPrefix)) {
- throw const Unauthenticated('${HttpHeaders.authorizationHeader} is null');
- }
- final Client client = httpClientProvider();
- final Oauth2Api oauth2api = Oauth2Api(client);
-
- // Get token from Google oauth
- final Tokeninfo info = await oauth2api.tokeninfo(
- idToken: idToken.substring(kBearerTokenPrefix.length),
- );
- if (info.expiresIn == null || info.expiresIn! < 1) {
- throw const Unauthenticated('Token is expired');
- }
-
- if (Config.allowedPubsubServiceAccounts.contains(info.email)) {
- return AuthenticatedContext(clientContext: clientContext);
- }
- throw Unauthenticated('${info.email} is not in allowedPubsubServiceAccounts');
- }
-}
diff --git a/app_dart/lib/src/request_handling/request_handler.dart b/app_dart/lib/src/request_handling/request_handler.dart
deleted file mode 100644
index 2577b4c..0000000
--- a/app_dart/lib/src/request_handling/request_handler.dart
+++ /dev/null
@@ -1,172 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-import 'dart:io';
-
-import 'package:http/http.dart' as http;
-import 'package:meta/meta.dart';
-
-import '../service/config.dart';
-import '../service/logging.dart';
-
-import 'body.dart';
-import 'exceptions.dart';
-
-/// A class that services HTTP requests and returns HTTP responses.
-///
-/// `T` is the type of object that is returned as the body of the HTTP response
-/// (before serialization). Subclasses whose HTTP responses don't include a
-/// body should extend `RequestHandler<Body>` and return null in their service
-/// handlers ([get] and [post]).
-
-abstract class RequestHandler<T extends Body> {
- /// Creates a new [RequestHandler].
- const RequestHandler({
- required this.config,
- });
-
- /// The global configuration of this AppEngine server.
- final Config config;
-
- /// Services an HTTP request.
- ///
- /// Subclasses should generally not override this method. Instead, they
- /// should override one of [get] or [post], depending on which methods
- /// they support.
- Future<void> service(
- HttpRequest request, {
- Future<void> Function(HttpStatusException)? onError,
- }) {
- return runZoned<Future<void>>(
- () async {
- final HttpResponse response = request.response;
- try {
- try {
- T body;
- switch (request.method) {
- case 'GET':
- body = await get();
- break;
- case 'POST':
- body = await post();
- break;
- default:
- throw MethodNotAllowed(request.method);
- }
- await _respond(body: body);
- httpClient?.close();
- return;
- } on HttpStatusException {
- rethrow;
- } catch (error, stackTrace) {
- log.severe('$error\n$stackTrace');
- throw InternalServerError('$error\n$stackTrace');
- }
- } on HttpStatusException catch (error) {
- if (onError != null) {
- await onError(error);
- }
- response
- ..statusCode = error.statusCode
- ..write(error.message);
- await response.flush();
- await response.close();
- return;
- }
- },
- zoneValues: <RequestKey<dynamic>, Object>{
- RequestKey.request: request,
- RequestKey.response: request.response,
- RequestKey.httpClient: httpClient ?? http.Client(),
- },
- );
- }
-
- /// Responds (using [response]) with the specified [status] and optional
- /// [body].
- ///
- /// Returns a future that completes when [response] has been closed.
- Future<void> _respond({int status = HttpStatus.ok, Body body = Body.empty}) async {
- response!.statusCode = status;
- await response!.addStream(body.serialize().cast<List<int>>());
- await response!.flush();
- await response!.close();
- }
-
- /// Gets the value associated with the specified [key] in the request
- /// context.
- ///
- /// Concrete subclasses should not call this directly. Instead, they should
- /// access the getters that are tied to specific keys, such as [request]
- /// and [response].
- ///
- /// If this is called outside the context of an HTTP request, this will
- /// throw a [StateError].
- @protected
- U? getValue<U>(RequestKey<U> key, {bool allowNull = false}) {
- final U? value = Zone.current[key] as U?;
- if (!allowNull && value == null) {
- throw StateError('Attempt to access ${key.name} while not in a request context');
- }
- return value;
- }
-
- /// Gets the current [HttpRequest].
- ///
- /// If this is called outside the context of an HTTP request, this will
- /// throw a [StateError].
- @protected
- HttpRequest? get request => getValue<HttpRequest>(RequestKey.request);
-
- /// Gets the current [HttpResponse].
- ///
- /// If this is called outside the context of an HTTP request, this will
- /// throw a [StateError].
- @protected
- HttpResponse? get response => getValue<HttpResponse>(RequestKey.response);
-
- /// Services an HTTP GET.
- ///
- /// Subclasses should override this method if they support GET requests.
- /// The default implementation will respond with HTTP 405 method not allowed.
- @protected
- Future<T> get() async {
- throw const MethodNotAllowed('GET');
- }
-
- /// Services an HTTP POST.
- ///
- /// Subclasses should override this method if they support POST requests.
- /// The default implementation will respond with HTTP 405 method not allowed.
- @protected
- Future<T> post() async {
- throw const MethodNotAllowed('POST');
- }
-
- /// The package:http Client to use for googleapis requests.
- @protected
- http.Client? get httpClient => getValue<http.Client>(
- RequestKey.httpClient,
- allowNull: true,
- );
-}
-
-/// A key that can be used to index a value within the request context.
-///
-/// Subclasses will only need to deal directly with this class if they add
-/// their own request context values.
-@protected
-class RequestKey<T> {
- const RequestKey(this.name);
-
- final String name;
-
- static const RequestKey<HttpRequest> request = RequestKey<HttpRequest>('request');
- static const RequestKey<HttpResponse> response = RequestKey<HttpResponse>('response');
- static const RequestKey<http.Client> httpClient = RequestKey<http.Client>('httpClient');
-
- @override
- String toString() => '$runtimeType($name)';
-}
diff --git a/app_dart/lib/src/request_handling/static_file_handler.dart b/app_dart/lib/src/request_handling/static_file_handler.dart
deleted file mode 100644
index 8eef08c..0000000
--- a/app_dart/lib/src/request_handling/static_file_handler.dart
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-import 'dart:io' show ContentType, HttpResponse;
-import 'dart:typed_data';
-
-import 'package:file/file.dart';
-import 'package:file/local.dart';
-import 'package:meta/meta.dart';
-import 'package:mime/mime.dart';
-import 'package:path/path.dart' as path;
-
-import '../../cocoon_service.dart';
-import 'exceptions.dart';
-
-/// A class based on [RequestHandler] for serving static files.
-@immutable
-class StaticFileHandler extends RequestHandler<Body> {
- /// Creates a new [StaticFileHandler].
- const StaticFileHandler(
- this.filePath, {
- required super.config,
- this.fs = const LocalFileSystem(),
- });
-
- /// The current [FileSystem] to retrieve files from.
- final FileSystem fs;
-
- /// The location of the static file to serve to the client.
- final String filePath;
-
- /// Services an HTTP GET Request for static files.
- @override
- Future<Body> get() async {
- final HttpResponse response = request!.response;
-
- /// The map of mimeTypes not found in [mime] package.
- final Map<String, String> mimeTypeMap = <String, String>{
- '.map': 'application/json',
- '': 'text/plain',
- '.smcbin': 'application/octet-stream',
- };
-
- final String resultPath = filePath == '/' ? '/index.html' : filePath;
-
- /// The file path in app_dart to the files to serve
- const String basePath = 'build/web';
- final File file = fs.file('$basePath$resultPath');
- if (file.existsSync()) {
- final String mimeType = mimeTypeMap.containsKey(path.extension(file.path))
- ? mimeTypeMap[path.extension(file.path)]!
- : lookupMimeType(resultPath)!;
- response.headers.contentType = ContentType.parse(mimeType);
- return Body.forStream(file.openRead().cast<Uint8List>());
- } else {
- throw NotFoundException(resultPath);
- }
- }
-}
diff --git a/app_dart/lib/src/request_handling/subscription_handler.dart b/app_dart/lib/src/request_handling/subscription_handler.dart
deleted file mode 100644
index b7cea2e..0000000
--- a/app_dart/lib/src/request_handling/subscription_handler.dart
+++ /dev/null
@@ -1,160 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-import 'dart:convert';
-import 'dart:io';
-import 'dart:typed_data';
-import 'package:meta/meta.dart';
-
-import '../model/luci/push_message.dart';
-import '../service/cache_service.dart';
-import '../service/logging.dart';
-import 'api_request_handler.dart';
-import 'authentication.dart';
-import 'body.dart';
-import 'exceptions.dart';
-import 'pubsub_authentication.dart';
-import 'request_handler.dart';
-
-/// An [ApiRequestHandler] that handles PubSub subscription messages.
-///
-/// Messages adhere to a specific contract, as follows:
-///
-/// * All requests must be authenticated per [AuthenticationProvider].
-/// * Request body is passed following the format of [PushMessageEnvelope].
-@immutable
-abstract class SubscriptionHandler extends RequestHandler<Body> {
- /// Creates a new [SubscriptionHandler].
- const SubscriptionHandler({
- required this.cache,
- required super.config,
- this.authProvider,
- required this.subscriptionName,
- });
-
- final CacheService cache;
-
- /// Service responsible for authenticating this [HttpRequest].
- final AuthenticationProvider? authProvider;
-
- /// Unique identifier of the PubSub subscription in this cloud project.
- final String subscriptionName;
-
- /// The authentication context associated with the HTTP request.
- ///
- /// This is guaranteed to be non-null. If the request was unauthenticated,
- /// the request will be denied.
- @protected
- AuthenticatedContext get authContext => getValue<AuthenticatedContext>(ApiKey.authContext)!;
-
- /// The [PushMessage] from this [HttpRequest].
- @protected
- PushMessage get message => getValue<PushMessage>(PubSubKey.message)!;
-
- @override
- Future<void> service(
- HttpRequest request, {
- Future<void> Function(HttpStatusException)? onError,
- }) async {
- AuthenticatedContext authContext;
- final AuthenticationProvider auth = authProvider ?? PubsubAuthenticationProvider(config: config);
- try {
- authContext = await auth.authenticate(request);
- } on Unauthenticated catch (error) {
- final HttpResponse response = request.response;
- response
- ..statusCode = HttpStatus.unauthorized
- ..write(error.message);
- await response.flush();
- await response.close();
- return;
- }
-
- List<int> body;
- try {
- body = await request.expand<int>((List<int> chunk) => chunk).toList();
- } catch (error) {
- final HttpResponse response = request.response;
- response
- ..statusCode = HttpStatus.internalServerError
- ..write('$error');
- await response.flush();
- await response.close();
- return;
- }
-
- log.fine('Request body: ${utf8.decode(body)}');
- PushMessageEnvelope? envelope;
- if (body.isNotEmpty) {
- try {
- final Map<String, dynamic> json = jsonDecode(utf8.decode(body)) as Map<String, dynamic>;
- envelope = PushMessageEnvelope.fromJson(json);
- } catch (error) {
- final HttpResponse response = request.response;
- response
- ..statusCode = HttpStatus.internalServerError
- ..write('$error');
- await response.flush();
- await response.close();
- return;
- }
- }
-
- if (envelope == null) {
- throw const BadRequestException('Failed to get message');
- }
- log.finer(envelope.toJson());
- log.fine('PubsubMessage publishTime: ${envelope.message!.publishTime}');
-
- final String messageId = envelope.message!.messageId!;
-
- final Uint8List? messageLock = await cache.getOrCreate(
- subscriptionName,
- messageId,
- createFn: null,
- );
- if (messageLock != null) {
- // No-op - There's already a write lock for this message
- final HttpResponse response = request.response
- ..statusCode = HttpStatus.ok
- ..write('$messageId was already processed');
- await response.flush();
- await response.close();
- return;
- }
-
- // Create a write lock in the cache to ensure requests are only processed once
- final Uint8List lockValue = Uint8List.fromList('l'.codeUnits);
- await cache.set(
- subscriptionName,
- messageId,
- lockValue,
- ttl: const Duration(days: 1),
- );
-
- log.info('Processing message $messageId');
- await runZoned<Future<void>>(
- () async => super.service(
- request,
- onError: (HttpStatusException exception) async {
- log.warning('Failed to process $message. (${exception.statusCode}) ${exception.message}');
- await cache.purge(subscriptionName, messageId);
- log.info('Purged write lock from cache');
- },
- ),
- zoneValues: <RequestKey<dynamic>, Object?>{
- PubSubKey.message: envelope.message,
- ApiKey.authContext: authContext,
- },
- );
- }
-}
-
-@visibleForTesting
-class PubSubKey<T> extends RequestKey<T> {
- const PubSubKey._(super.name);
-
- static const PubSubKey<PushMessage> message = PubSubKey<PushMessage>._('message');
-}
diff --git a/app_dart/lib/src/request_handling/swarming_authentication.dart b/app_dart/lib/src/request_handling/swarming_authentication.dart
deleted file mode 100644
index 8d77f9f..0000000
--- a/app_dart/lib/src/request_handling/swarming_authentication.dart
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-import 'dart:convert';
-import 'dart:io';
-
-import 'package:appengine/appengine.dart';
-import 'package:http/http.dart';
-import 'package:meta/meta.dart';
-
-import '../../cocoon_service.dart';
-import '../foundation/providers.dart';
-import '../model/google/token_info.dart';
-import '../service/logging.dart';
-import 'exceptions.dart';
-
-/// Class capable of authenticating [HttpRequest]s for infra endpoints.
-///
-/// This class implements an ACL on a [RequestHandler] to ensure only automated
-/// systems can access the endpoints.
-///
-/// If the request has the `Service-Account-Token` HTTP header, the token
-/// will be authenticated as a LUCI bot. This token is validated against
-/// Google Auth APIs.
-///
-/// If none of the above authentication methods yield an authenticated
-/// request, then the request is unauthenticated, and any call to
-/// [authenticate] will throw an [Unauthenticated] exception.
-@immutable
-class SwarmingAuthenticationProvider extends AuthenticationProvider {
- const SwarmingAuthenticationProvider({
- required super.config,
- super.clientContextProvider = Providers.serviceScopeContext,
- super.httpClientProvider = Providers.freshHttpClient,
- });
-
- /// Name of the header that LUCI requests will put their service account token.
- static const String kSwarmingTokenHeader = 'Service-Account-Token';
-
- /// Authenticates the specified [request] and returns the associated
- /// [AuthenticatedContext].
- ///
- /// See the class documentation on [AuthenticationProvider] for a discussion
- /// of the different types of authentication that are accepted.
- ///
- /// This will throw an [Unauthenticated] exception if the request is
- /// unauthenticated.
- @override
- Future<AuthenticatedContext> authenticate(HttpRequest request) async {
- final String? swarmingToken = request.headers.value(kSwarmingTokenHeader);
-
- final ClientContext clientContext = clientContextProvider();
-
- if (swarmingToken != null) {
- log.fine('Authenticating as swarming task');
- return authenticateAccessToken(swarmingToken, clientContext: clientContext);
- }
-
- throw const Unauthenticated('Request rejected due to not from LUCI');
- }
-
- /// Authenticate [accessToken] against Google OAuth 2 API.
- ///
- /// Access tokens are the legacy authentication strategy for Google OAuth, where ID tokens
- /// are the new technique to use. LUCI auth only generates access tokens, and must be
- /// validated against a different endpoint. We only authenticate access tokens
- /// if they belong to a LUCI prod service account.
- ///
- /// If LUCI auth adds id tokens, we can switch to that and remove this.
- Future<AuthenticatedContext> authenticateAccessToken(
- String accessToken, {
- required ClientContext clientContext,
- }) async {
- // Authenticate as a signed-in Google account via OAuth id token.
- final Client client = httpClientProvider();
- try {
- log.fine('Sending token request to Google OAuth');
- final Response verifyTokenResponse = await client.get(
- Uri.https(
- 'oauth2.googleapis.com',
- '/tokeninfo',
- <String, String>{
- 'access_token': accessToken,
- },
- ),
- );
-
- if (verifyTokenResponse.statusCode != HttpStatus.ok) {
- /// Google Auth API returns a message in the response body explaining why
- /// the request failed. Such as "Invalid Token".
- final String body = verifyTokenResponse.body;
- log.warning('Token verification failed: ${verifyTokenResponse.statusCode}; $body');
- throw const Unauthenticated('Invalid access token');
- }
-
- TokenInfo token;
- try {
- token = TokenInfo.fromJson(json.decode(verifyTokenResponse.body) as Map<String, dynamic>);
- } on FormatException {
- log.warning('Failed to decode token JSON: ${verifyTokenResponse.body}');
- throw InternalServerError('Invalid JSON: "${verifyTokenResponse.body}"');
- }
-
- // Update is from Flutter LUCI builds
- if (token.email == Config.luciProdAccount) {
- return AuthenticatedContext(clientContext: clientContext);
- }
-
- if (token.email == Config.frobAccount) {
- log.fine('Authenticating as FRoB request');
- return AuthenticatedContext(clientContext: clientContext);
- }
-
- log.fine(verifyTokenResponse.body);
- log.warning('${token.email} is not allowed');
- throw Unauthenticated('${token.email} is not allowed');
- } finally {
- client.close();
- }
- }
-}
diff --git a/app_dart/lib/src/service/access_client_provider.dart b/app_dart/lib/src/service/access_client_provider.dart
deleted file mode 100644
index fb259d1..0000000
--- a/app_dart/lib/src/service/access_client_provider.dart
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:googleapis_auth/auth_io.dart';
-import 'package:http/http.dart';
-
-class AccessClientProvider {
- /// Returns an OAuth 2.0 authenticated access client for the device lab service account.
- Future<Client> createAccessClient({
- List<String> scopes = const <String>['https://www.googleapis.com/auth/cloud-platform'],
- }) async {
- return clientViaApplicationDefaultCredentials(scopes: scopes);
- }
-}
diff --git a/app_dart/lib/src/service/access_token_provider.dart b/app_dart/lib/src/service/access_token_provider.dart
deleted file mode 100644
index 69f2621..0000000
--- a/app_dart/lib/src/service/access_token_provider.dart
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:googleapis_auth/auth_io.dart';
-import 'package:http/http.dart' as http;
-
-import 'config.dart';
-
-/// Function signature for a [TaskService] provider.
-typedef AccessTokenServiceProvider = AccessTokenService Function(Config config);
-
-/// A provider for Oauth2 tokens from Service Account JSON.
-class AccessTokenService {
- /// Creates a new Access Token provider.
- const AccessTokenService(this.config);
-
- /// The Cocoon configuration.
- final Config config;
-
- /// Creates and returns a [AccessTokenService] using a [Config] object.
- static AccessTokenService defaultProvider(Config config) {
- return AccessTokenService(config);
- }
-
- /// Returns an OAuth 2.0 access token for the device lab service account.
- Future<AccessToken> createAccessToken() async {
- final http.Client httpClient = http.Client();
- try {
- final AccessCredentials credentials = await obtainAccessCredentialsViaMetadataServer(httpClient);
- return credentials.accessToken;
- } finally {
- httpClient.close();
- }
- }
-}
diff --git a/app_dart/lib/src/service/bigquery.dart b/app_dart/lib/src/service/bigquery.dart
deleted file mode 100644
index 2b32870..0000000
--- a/app_dart/lib/src/service/bigquery.dart
+++ /dev/null
@@ -1,248 +0,0 @@
-// Copyright 2020 The Flutter 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:async';
-
-import 'package:googleapis/bigquery/v2.dart';
-import 'package:http/http.dart';
-
-import 'access_client_provider.dart';
-
-/// The sql query to query the build statistic from the
-/// `flutter-dashboard.datasite.luci_prod_build_status`.
-///
-/// The schema of the `luci_prod_build_status` table:
-/// time TIMESTAMP
-/// date DATE
-/// sha STRING
-/// flaky_builds STRING
-/// succeeded_builds STRING
-/// branch STRING
-/// device_os STRING
-/// pool STRING
-/// repo STRING
-/// builder_name STRING
-/// success_count INTEGER
-/// failure_count INTEGER
-/// is_flaky INTEGER
-///
-/// This returns latest [LIMIT] number of build stats for each builder.
-const String getBuilderStatisticQuery = r'''
-select builder_name,
- sum(is_flaky) as flaky_number,
- count(*) as total_number,
- string_agg(case when is_flaky = 1 then flaky_builds end, ', ') as flaky_builds,
- string_agg(succeeded_builds, ', ') as succeeded_builds,
- array_agg(case when is_flaky = 1 then sha end IGNORE NULLS ORDER BY date DESC)[ordinal(1)] as recent_flaky_commit,
- array_agg(case when is_flaky = 1 then flaky_builds end IGNORE NULLS ORDER BY date DESC)[ordinal(1)] as flaky_build_of_recent_flaky_commit,
- sum(is_flaky)/count(*) as flaky_ratio,
- min(date) as from_date,
- max(date) as to_date
-from (select *, row_number() over (partition by builder_name order by time desc) as rank from `flutter-dashboard.datasite.luci_prod_build_status`)
-where date>=date_sub(current_date(), interval 30 day) and
- builder_name not like '%Drone' and
- repo='flutter' and
- branch='master' and
- pool = 'luci.flutter.prod' and
- builder_name not like '%Beta%' and
- builder_name not like '% beta %' and
- builder_name not like '%Stable%' and
- builder_name not like '% stable %' and
- builder_name not like '%Dev%' and
- builder_name not like '% dev %' and
- rank<=@LIMIT
-group by builder_name;
-''';
-
-const String getStagingBuilderStatisticQuery = r'''
-select builder_name,
- sum(is_flaky) as flaky_number,
- count(*) as total_number,
- string_agg(case when is_flaky = 1 then flaky_builds end, ', ') as flaky_builds,
- string_agg(succeeded_builds, ', ') as succeeded_builds,
- array_agg(case when is_flaky = 1 then sha end IGNORE NULLS ORDER BY date DESC)[ordinal(1)] as recent_flaky_commit,
- array_agg(case when is_flaky = 1 then flaky_builds end IGNORE NULLS ORDER BY date DESC)[ordinal(1)] as flaky_build_of_recent_flaky_commit,
- sum(is_flaky)/count(*) as flaky_ratio,
- min(date) as from_date,
- max(date) as to_date
-from (select *, row_number() over (partition by builder_name order by time desc) as rank from `flutter-dashboard.datasite.luci_staging_build_status`)
-where date>=date_sub(current_date(), interval 30 day) and
- builder_name not like '%Drone' and
- repo='flutter' and
- branch='master' and
- pool = 'luci.flutter.staging' and
- builder_name not like '%Beta%' and
- builder_name not like '% beta %' and
- rank<=@LIMIT
-group by builder_name;
-''';
-
-// Returns builds in the past 30 days to exclude obsolete historical data.
-const String getRecordsQuery = r'''
-select sha, is_flaky, failure_count from `flutter-dashboard.datasite.luci_staging_build_status`
-where builder_name=@BUILDER_NAME and date>=date_sub(current_date(), interval 30 day)
-order by time desc
-limit @LIMIT
-''';
-
-class BigqueryService {
- const BigqueryService(this.accessClientProvider);
-
- /// AccessClientProvider for OAuth 2.0 authenticated access client
- final AccessClientProvider accessClientProvider;
-
- /// Return a [TabledataResource] with an authenticated [client]
- Future<TabledataResource> defaultTabledata() async {
- final Client client = await accessClientProvider.createAccessClient(
- scopes: const <String>[BigqueryApi.bigqueryScope],
- );
- return BigqueryApi(client).tabledata;
- }
-
- /// Return a [JobsResource] with an authenticated [client]
- Future<JobsResource> defaultJobs() async {
- final Client client = await accessClientProvider.createAccessClient(
- scopes: const <String>[BigqueryApi.bigqueryScope],
- );
- return BigqueryApi(client).jobs;
- }
-
- /// Return the top [limit] number of current builder statistic.
- ///
- /// See getBuilderStatisticQuery to get the detail information about the table
- /// schema
- Future<List<BuilderStatistic>> listBuilderStatistic(
- String projectId, {
- int limit = 100,
- String bucket = 'prod',
- }) async {
- final JobsResource jobsResource = await defaultJobs();
- final QueryRequest query = QueryRequest.fromJson(<String, Object>{
- 'query': bucket == 'staging' ? getStagingBuilderStatisticQuery : getBuilderStatisticQuery,
- 'queryParameters': <Map<String, Object>>[
- <String, Object>{
- 'name': 'LIMIT',
- 'parameterType': <String, Object>{'type': 'INT64'},
- 'parameterValue': <String, Object>{'value': '$limit'},
- },
- ],
- 'useLegacySql': false,
- });
- final QueryResponse response = await jobsResource.query(query, projectId);
- if (!response.jobComplete!) {
- throw 'job does not complete';
- }
- final List<BuilderStatistic> result = <BuilderStatistic>[];
- for (final TableRow row in response.rows!) {
- final String builder = row.f![0].v as String;
- List<String>? flakyBuilds = (row.f![3].v as String?)?.split(', ');
- flakyBuilds?.sort();
- flakyBuilds = flakyBuilds?.reversed.toList();
- List<String>? succeededBuilds = (row.f![4].v as String?)?.split(', ');
- succeededBuilds?.sort();
- succeededBuilds = succeededBuilds?.reversed.toList();
- result.add(
- BuilderStatistic(
- name: builder,
- flakyRate: double.parse(row.f![7].v as String),
- flakyBuilds: flakyBuilds ?? const <String>[],
- succeededBuilds: succeededBuilds ?? const <String>[],
- recentCommit: row.f![5].v as String?,
- flakyBuildOfRecentCommit: row.f![6].v as String?,
- flakyNumber: int.parse(row.f![1].v as String),
- totalNumber: int.parse(row.f![2].v as String),
- fromDate: row.f![8].v as String?,
- toDate: row.f![9].v as String?,
- ),
- );
- }
- return result;
- }
-
- /// Return the list of current builder statistic.
- ///
- /// See getBuilderStatisticQuery to get the detail information about the table
- /// schema
- Future<List<BuilderRecord>> listRecentBuildRecordsForBuilder(
- String projectId, {
- String? builder,
- int? limit,
- }) async {
- final JobsResource jobsResource = await defaultJobs();
- final QueryRequest query = QueryRequest.fromJson(<String, Object>{
- 'query': getRecordsQuery,
- 'parameterMode': 'NAMED',
- 'queryParameters': <Map<String, Object>>[
- <String, Object>{
- 'name': 'BUILDER_NAME',
- 'parameterType': <String, Object>{'type': 'STRING'},
- 'parameterValue': <String, Object?>{'value': builder},
- },
- <String, Object>{
- 'name': 'LIMIT',
- 'parameterType': <String, Object>{'type': 'INT64'},
- 'parameterValue': <String, Object>{'value': '$limit'},
- },
- ],
- 'useLegacySql': false,
- });
- final QueryResponse response = await jobsResource.query(query, projectId);
- if (!response.jobComplete!) {
- throw 'job does not complete';
- }
- final List<BuilderRecord> result = <BuilderRecord>[];
- // When a test is newly marked as flaky, it is possible no execution exists.
- if (response.rows == null) {
- return result;
- }
- for (final TableRow row in response.rows!) {
- result.add(
- BuilderRecord(
- commit: row.f![0].v as String,
- isFlaky: row.f![1].v as String != '0',
- isFailed: row.f![2].v as String != '0',
- ),
- );
- }
- return result;
- }
-}
-
-class BuilderRecord {
- BuilderRecord({
- required this.commit,
- required this.isFlaky,
- required this.isFailed,
- });
-
- final String commit;
- final bool isFlaky;
- final bool isFailed;
-}
-
-class BuilderStatistic {
- BuilderStatistic({
- required this.name,
- required this.flakyRate,
- required this.flakyNumber,
- required this.totalNumber,
- this.flakyBuilds,
- this.succeededBuilds,
- this.recentCommit,
- this.flakyBuildOfRecentCommit,
- this.fromDate,
- this.toDate,
- });
-
- final String name;
- final double flakyRate;
- final List<String>? flakyBuilds;
- final List<String>? succeededBuilds;
- final String? recentCommit;
- final String? flakyBuildOfRecentCommit;
- final int flakyNumber;
- final int totalNumber;
- final String? fromDate;
- final String? toDate;
-}
diff --git a/app_dart/lib/src/service/branch_service.dart b/app_dart/lib/src/service/branch_service.dart
deleted file mode 100644
index a6072d9..0000000
--- a/app_dart/lib/src/service/branch_service.dart
+++ /dev/null
@@ -1,171 +0,0 @@
-// Copyright 2021 The Flutter 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:async';
-
-import 'package:cocoon_service/src/service/config.dart';
-import 'package:cocoon_service/src/service/github_service.dart';
-import 'package:github/github.dart' as gh;
-import 'package:retry/retry.dart';
-
-import '../model/gerrit/commit.dart';
-import '../request_handling/exceptions.dart';
-import 'gerrit_service.dart';
-import 'logging.dart';
-
-class RetryException implements Exception {}
-
-/// A class to manage GitHub branches.
-///
-/// Track branch activities such as branch creation, and helps manage release branches.
-class BranchService {
- BranchService({
- required this.config,
- required this.gerritService,
- this.retryOptions = const RetryOptions(maxAttempts: 3),
- });
-
- final Config config;
- final GerritService gerritService;
- final RetryOptions retryOptions;
-
- /// Creates a flutter/recipes branch that aligns to a flutter/engine commit.
- ///
- /// Take the example repo history:
- /// flutter/engine: A -> B -> C -> D -> E
- /// flutter/recipes: V -> W -> X -> Y -> Z
- ///
- /// If flutter/engine branches at C, this finds the flutter/recipes commit that should be used for C.
- /// The best guess for a flutter/recipes commit that aligns with C is whatever was the most recently committed
- /// before C was committed.
- ///
- /// Once the flutter/recipes commit is found, it is branched to match flutter/engine.
- ///
- /// Generally, this should work. However, some edge cases may require CPs. Such as when commits land in a
- /// short timespan, and require the release manager to CP onto the recipes branch (in the case of reverts).
- Future<void> branchFlutterRecipes(String branch, String engineSha) async {
- final gh.RepositorySlug recipesSlug = gh.RepositorySlug('flutter', 'recipes');
- if ((await gerritService.branches(
- '${recipesSlug.owner}-review.googlesource.com',
- recipesSlug.name,
- filterRegex: branch,
- ))
- .contains(branch)) {
- // subString is a regex, and can return multiple matches
- log.warning('$branch already exists for $recipesSlug');
- throw BadRequestException('$branch already exists');
- }
- final Iterable<GerritCommit> recipeCommits =
- await gerritService.commits(recipesSlug, Config.defaultBranch(recipesSlug));
- log.info('$recipesSlug commits: $recipeCommits');
- final gh.RepositoryCommit engineCommit = await retryOptions.retry(
- () async {
- final GithubService githubService = await config.createDefaultGitHubService();
- return githubService.github.repositories.getCommit(Config.engineSlug, engineSha);
- },
- retryIf: (Exception e) => e is gh.GitHubError,
- );
- log.info('${Config.engineSlug} commit: $engineCommit');
- final DateTime? branchTime = engineCommit.commit?.committer?.date;
- if (branchTime == null) {
- throw BadRequestException('$engineSha has no commit time');
- }
- log.info('Searching for a recipe commit before $branchTime');
- for (GerritCommit recipeCommit in recipeCommits) {
- final DateTime? recipeTime = recipeCommit.author?.time;
-
- if (recipeTime != null && recipeTime.isBefore(branchTime)) {
- final String revision = recipeCommit.commit!;
- return gerritService.createBranch(recipesSlug, branch, revision);
- }
- }
-
- throw InternalServerError('Failed to find a revision to flutter/recipes for $branch before $branchTime');
- }
-
- /// Returns a Map that contains the latest google3 roll, beta, and stable branches.
- ///
- /// Latest beta and stable branches are retrieved based on 'beta' and 'stable' tags. Dev branch is retrived
- /// as the latest flutter candidate branch.
- Future<List<Map<String, String>>> getReleaseBranches({
- required GithubService githubService,
- required gh.RepositorySlug slug,
- }) async {
- final List<gh.Branch> branches = await githubService.github.repositories.listBranches(slug).toList();
- final String latestCandidateBranch = await _getLatestCandidateBranch(
- github: githubService.github,
- slug: slug,
- branches: branches,
- );
-
- final String betaName = await _getBranchNameFromFile(
- githubService: githubService,
- slug: slug,
- branchName: 'beta',
- );
- final String stableName = await _getBranchNameFromFile(
- githubService: githubService,
- slug: slug,
- branchName: 'stable',
- );
- return <Map<String, String>>[
- {
- 'branch': Config.defaultBranch(slug),
- 'name': 'HEAD',
- },
- {
- 'branch': stableName,
- 'name': 'stable',
- },
- {
- 'branch': betaName,
- 'name': 'beta',
- },
- {
- 'branch': latestCandidateBranch,
- 'name': 'dev',
- },
- ];
- }
-
- Future<String> _getBranchNameFromFile({
- required GithubService githubService,
- required gh.RepositorySlug slug,
- required String branchName,
- }) async {
- return (await githubService.getFileContent(
- slug,
- 'bin/internal/release-candidate-branch.version',
- ref: branchName,
- ))
- .trim();
- }
-
- /// Retrieve the latest canidate branch from all candidate branches.
- Future<String> _getLatestCandidateBranch({
- required gh.GitHub github,
- required gh.RepositorySlug slug,
- required List<gh.Branch> branches,
- }) async {
- final RegExp candidateBranchName = RegExp(r'^flutter-\d+\.\d+-candidate\.\d+$');
- final List<gh.Branch> devBranches = branches.where((gh.Branch b) => candidateBranchName.hasMatch(b.name!)).toList();
- devBranches.sort((b, a) => (_versionSum(a.name!)).compareTo(_versionSum(b.name!)));
- final String devBranchName = devBranches.take(1).single.name!;
- return devBranchName;
- }
-
- /// Helper function to convert candidate branch versions to numbers for comparison.
- int _versionSum(String tagOrBranchName) {
- final List<String> digits = tagOrBranchName.replaceAll(r'flutter|candidate', '0').split(RegExp(r'\.|\-'));
- int versionSum = 0;
- for (String digit in digits) {
- final int? d = int.tryParse(digit);
- if (d == null) {
- continue;
- }
- versionSum = versionSum * 100 + d;
- }
- return versionSum;
- }
-}
diff --git a/app_dart/lib/src/service/build_status_provider.dart b/app_dart/lib/src/service/build_status_provider.dart
deleted file mode 100644
index 8f1fc63..0000000
--- a/app_dart/lib/src/service/build_status_provider.dart
+++ /dev/null
@@ -1,222 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-
-import 'package:github/github.dart';
-import 'package:meta/meta.dart';
-
-import '../model/appengine/commit.dart';
-import '../model/appengine/github_build_status_update.dart';
-import '../model/appengine/stage.dart';
-import '../model/appengine/task.dart';
-import 'datastore.dart';
-
-/// Function signature for a [BuildStatusService] provider.
-typedef BuildStatusServiceProvider = BuildStatusService Function(DatastoreService datastoreService);
-
-/// Branches that are used to calculate the tree status.
-const Set<String> defaultBranches = <String>{'refs/heads/main', 'refs/heads/master'};
-
-/// Class that calculates the current build status.
-class BuildStatusService {
- const BuildStatusService(this.datastoreService);
-
- final DatastoreService datastoreService;
-
- /// Creates and returns a [DatastoreService] using [db] and [maxEntityGroups].
- static BuildStatusService defaultProvider(DatastoreService datastoreService) {
- return BuildStatusService(datastoreService);
- }
-
- @visibleForTesting
- static const int numberOfCommitsToReferenceForTreeStatus = 20;
-
- /// Calculates and returns the "overall" status of the Flutter build.
- ///
- /// This calculation operates by looking for the most recent success or
- /// failure for every (non-flaky) task in the manifest.
- ///
- /// Take the example build dashboard below:
- /// ✔ = passed, ✖ = failed, ☐ = new, ░ = in progress, s = skipped
- /// +---+---+---+---+
- /// | A | B | C | D |
- /// +---+---+---+---+
- /// | ✔ | ☐ | ░ | s |
- /// +---+---+---+---+
- /// | ✔ | ░ | ✔ | ✖ |
- /// +---+---+---+---+
- /// | ✔ | ✖ | ✔ | ✔ |
- /// +---+---+---+---+
- /// This build will fail because of Task B only. Task D is not included in
- /// the latest commit status, so it does not impact the build status.
- /// Task B fails because its last known status was to be failing, even though
- /// there is currently a newer version that is in progress.
- ///
- /// Tree status is only for [defaultBranches].
- Future<BuildStatus?> calculateCumulativeStatus(RepositorySlug slug) async {
- final List<CommitStatus> statuses = await retrieveCommitStatus(
- limit: numberOfCommitsToReferenceForTreeStatus,
- slug: slug,
- ).toList();
- if (statuses.isEmpty) {
- return BuildStatus.failure();
- }
-
- final Map<String, bool> tasksInProgress = _findTasksRelevantToLatestStatus(statuses);
- if (tasksInProgress.isEmpty) {
- return BuildStatus.failure();
- }
-
- final List<String> failedTasks = <String>[];
- for (CommitStatus status in statuses) {
- for (Stage stage in status.stages) {
- for (Task task in stage.tasks) {
- /// If a task [isRelevantToLatestStatus] but has not run yet, we look
- /// for a previous run of the task from the previous commit.
- final bool isRelevantToLatestStatus = tasksInProgress.containsKey(task.name);
-
- /// Tasks that are not relevant to the latest status will have a
- /// null value in the map.
- final bool taskInProgress = tasksInProgress[task.name] ?? true;
-
- if (isRelevantToLatestStatus && taskInProgress) {
- if (task.isFlaky! || _isSuccessful(task)) {
- /// This task no longer needs to be checked to see if it causing
- /// the build status to fail.
- tasksInProgress[task.name!] = false;
- } else if (_isFailed(task) || _isRerunning(task)) {
- failedTasks.add(task.name!);
-
- /// This task no longer needs to be checked to see if its causing
- /// the build status to fail since its been
- /// added to the failedTasks list.
- tasksInProgress[task.name!] = false;
- }
- }
- }
- }
- }
- return failedTasks.isNotEmpty ? BuildStatus.failure(failedTasks) : BuildStatus.success();
- }
-
- /// Creates a map of the tasks that need to be checked for the build status.
- ///
- /// This is based on the most recent [CommitStatus] and all of its tasks.
- Map<String, bool> _findTasksRelevantToLatestStatus(List<CommitStatus> statuses) {
- final Map<String, bool> tasks = <String, bool>{};
-
- for (Stage stage in statuses.first.stages) {
- for (Task task in stage.tasks) {
- tasks[task.name!] = true;
- }
- }
-
- return tasks;
- }
-
- /// Retrieves the comprehensive status of every task that runs per commit.
- ///
- /// The returned stream will be ordered by most recent commit first, then
- /// the next newest, and so on.
- Stream<CommitStatus> retrieveCommitStatus({
- required int limit,
- int? timestamp,
- String? branch,
- required RepositorySlug slug,
- }) async* {
- await for (Commit commit in datastoreService.queryRecentCommits(
- limit: limit,
- timestamp: timestamp,
- branch: branch,
- slug: slug,
- )) {
- final List<Stage> stages = await datastoreService.queryTasksGroupedByStage(commit);
- yield CommitStatus(commit, stages);
- }
- }
-
- bool _isFailed(Task task) {
- return task.status == Task.statusFailed ||
- task.status == Task.statusInfraFailure ||
- task.status == Task.statusCancelled;
- }
-
- bool _isSuccessful(Task task) {
- return task.status == Task.statusSucceeded;
- }
-
- bool _isRerunning(Task task) {
- return task.attempts! > 1 && (task.status == Task.statusInProgress || task.status == Task.statusNew);
- }
-}
-
-/// Class that holds the status for all tasks corresponding to a particular
-/// commit.
-///
-/// Tasks may still be running, and thus their status is subject to change.
-/// Put another way, this class holds information that is a snapshot in time.
-@immutable
-class CommitStatus {
- /// Creates a new [CommitStatus].
- const CommitStatus(this.commit, this.stages);
-
- /// The commit against which all the tasks in [stages] are run.
- final Commit commit;
-
- /// The partitioned stages, each of which holds a bucket of tasks that
- /// belong in the stage.
- final List<Stage> stages;
-}
-
-@immutable
-class BuildStatus {
- const BuildStatus._(this.value, [this.failedTasks = const <String>[]])
- : assert(value == GithubBuildStatusUpdate.statusSuccess || value == GithubBuildStatusUpdate.statusFailure);
- factory BuildStatus.success() => const BuildStatus._(GithubBuildStatusUpdate.statusSuccess);
- factory BuildStatus.failure([List<String> failedTasks = const <String>[]]) =>
- BuildStatus._(GithubBuildStatusUpdate.statusFailure, failedTasks);
-
- final String value;
- final List<String> failedTasks;
-
- bool get succeeded {
- return value == GithubBuildStatusUpdate.statusSuccess;
- }
-
- String get githubStatus => value;
-
- @override
- int get hashCode {
- int hash = 17;
- hash = hash * 31 + value.hashCode;
- hash = hash * 31 + failedTasks.hashCode;
- return hash;
- }
-
- @override
- bool operator ==(Object other) {
- if (identical(this, other)) {
- return true;
- }
- if (other is BuildStatus) {
- if (value != other.value) {
- return false;
- }
- if (other.failedTasks.length != failedTasks.length) {
- return false;
- }
- for (int i = 0; i < failedTasks.length; ++i) {
- if (failedTasks[i] != other.failedTasks[i]) {
- return false;
- }
- }
- return true;
- }
- return false;
- }
-
- @override
- String toString() => '$value $failedTasks';
-}
diff --git a/app_dart/lib/src/service/buildbucket.dart b/app_dart/lib/src/service/buildbucket.dart
deleted file mode 100644
index 873dab2..0000000
--- a/app_dart/lib/src/service/buildbucket.dart
+++ /dev/null
@@ -1,210 +0,0 @@
-// Copyright 2019 The Flutter 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:convert';
-import 'dart:io';
-
-import 'package:googleapis_auth/auth_io.dart';
-import 'package:http/http.dart' as http;
-import 'package:meta/meta.dart';
-
-import '../model/luci/buildbucket.dart';
-import '../request_handling/body.dart';
-import 'access_token_provider.dart';
-
-import '../service/logging.dart';
-
-/// A client interface to LUCI BuildBucket
-@immutable
-class BuildBucketClient {
- /// Creates a new build bucket Client.
- ///
- /// The [buildBucketUri] parameter must not be null, and will be defaulted to
- /// [kDefaultBuildBucketUri] if not specified.
- ///
- /// The [httpClient] parameter will be defaulted to `HttpClient()` if not
- /// specified or null.
- BuildBucketClient({
- this.buildBucketBuilderUri = kDefaultBuildBucketBuilderUri,
- this.buildBucketBuildUri = kDefaultBuildBucketBuildUri,
- this.accessTokenService,
- http.Client? httpClient,
- }) : httpClient = httpClient ?? http.Client();
-
- /// Garbage to prevent browser/JSON parsing exploits.
- static const String kRpcResponseGarbage = ")]}'";
-
- /// The default endpoint for BuildBucket build requests.
- static const String kDefaultBuildBucketBuildUri = 'https://cr-buildbucket.appspot.com/prpc/buildbucket.v2.Builds';
-
- /// The default endpoint for BuildBucket builder requests.
- static const String kDefaultBuildBucketBuilderUri = 'https://cr-buildbucket.appspot.com/prpc/buildbucket.v2.Builders';
-
- /// The base URI for build bucket requests.
- ///
- /// Defaults to [kDefaultBuildBucketBuildUri].
- final String buildBucketBuildUri;
-
- /// The base URI for build bucket requests.
- ///
- /// Defaults to [kDefaultBuildBucketBuilderUri].
- final String buildBucketBuilderUri;
-
- /// The token provider for OAuth2 requests.
- ///
- /// If this is non-null, an access token will be attached to any outbound
- /// HTTP requests issued by this client.
- final AccessTokenService? accessTokenService;
-
- /// The [http.Client] to use for requests.
- final http.Client httpClient;
-
- Future<T> _postRequest<S extends JsonBody, T>(
- String path,
- S request,
- T Function(Map<String, dynamic>? rawResponse) responseFromJson, {
- String buildBucketUri = kDefaultBuildBucketBuildUri,
- }) async {
- final Uri url = Uri.parse('$buildBucketUri$path');
- final AccessToken? token = await accessTokenService?.createAccessToken();
-
- log.fine('Making request with path: $url and body: ${json.encode(request)}');
-
- final http.Response response = await httpClient.post(
- url,
- body: json.encode(request),
- headers: <String, String>{
- HttpHeaders.contentTypeHeader: 'application/json',
- HttpHeaders.acceptHeader: 'application/json',
- if (token != null) HttpHeaders.authorizationHeader: '${token.type} ${token.data}',
- },
- );
-
- if (response.statusCode < 300) {
- return responseFromJson(
- json.decode(response.body.substring(kRpcResponseGarbage.length)) as Map<String, dynamic>?,
- );
- }
- throw BuildBucketException(response.statusCode, response.body);
- }
-
- /// The RPC request to schedule a build.
- Future<Build> scheduleBuild(
- ScheduleBuildRequest request, {
- String buildBucketUri = kDefaultBuildBucketBuildUri,
- }) {
- return _postRequest<ScheduleBuildRequest, Build>(
- '/ScheduleBuild',
- request,
- Build.fromJson,
- buildBucketUri: buildBucketUri,
- );
- }
-
- /// The RPC request to search for builds.
- Future<SearchBuildsResponse> searchBuilds(
- SearchBuildsRequest request, {
- String buildBucketUri = kDefaultBuildBucketBuildUri,
- }) {
- return _postRequest<SearchBuildsRequest, SearchBuildsResponse>(
- '/SearchBuilds',
- request,
- SearchBuildsResponse.fromJson,
- buildBucketUri: buildBucketUri,
- );
- }
-
- /// The RPC method to batch multiple RPC methods in a single HTTP request.
- ///
- /// The response is guaranteed to contain line-item responses for all
- /// line-item requests that were issued in [request]. If only a subset of
- /// responses were retrieved, a [BatchRequestException] will be thrown.
- Future<BatchResponse> batch(
- BatchRequest request, {
- String buildBucketUri = kDefaultBuildBucketBuildUri,
- }) async {
- final BatchResponse response = await _postRequest<BatchRequest, BatchResponse>(
- '/Batch',
- request,
- BatchResponse.fromJson,
- buildBucketUri: buildBucketUri,
- );
- if (response.responses!.length != request.requests!.length) {
- throw BatchRequestException('Failed to execute all requests');
- }
- return response;
- }
-
- /// The RPC request to cancel a build.
- Future<Build> cancelBuild(
- CancelBuildRequest request, {
- String buildBucketUri = kDefaultBuildBucketBuildUri,
- }) {
- return _postRequest<CancelBuildRequest, Build>(
- '/CancelBuild',
- request,
- Build.fromJson,
- buildBucketUri: buildBucketUri,
- );
- }
-
- /// The RPC request to get details about a build.
- Future<Build> getBuild(
- GetBuildRequest request, {
- String buildBucketUri = kDefaultBuildBucketBuildUri,
- }) {
- return _postRequest<GetBuildRequest, Build>(
- '/GetBuild',
- request,
- Build.fromJson,
- buildBucketUri: buildBucketUri,
- );
- }
-
- /// The RPC request to get a list of builders.
- Future<ListBuildersResponse> listBuilders(
- ListBuildersRequest request, {
- String buildBucketUri = kDefaultBuildBucketBuilderUri,
- }) {
- return _postRequest<ListBuildersRequest, ListBuildersResponse>(
- '/ListBuilders',
- request,
- ListBuildersResponse.fromJson,
- buildBucketUri: buildBucketUri,
- );
- }
-
- /// Closes the underlying [HttpClient].
- ///
- /// If `force` is true, it will close immediately and cause outstanding
- /// requests to end with an error. Otherwise, it will wait for outstanding
- /// requests to finish before closing.
- ///
- /// Once this call completes, additional RPC requests will throw an exception.
- void close() {
- httpClient.close();
- }
-}
-
-class BuildBucketException implements Exception {
- const BuildBucketException(this.statusCode, this.message);
-
- /// The HTTP status code of the error.
- final int statusCode;
-
- /// The message from the server.
- final String message;
-
- @override
- String toString() => '$runtimeType: [$statusCode]: $message';
-}
-
-class BatchRequestException implements Exception {
- BatchRequestException(this.message);
-
- final String message;
-
- @override
- String toString() => message;
-}
diff --git a/app_dart/lib/src/service/cache_service.dart b/app_dart/lib/src/service/cache_service.dart
deleted file mode 100644
index ba172fb..0000000
--- a/app_dart/lib/src/service/cache_service.dart
+++ /dev/null
@@ -1,217 +0,0 @@
-// Copyright 2019 The Flutter 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:convert';
-import 'dart:typed_data';
-
-import 'package:meta/meta.dart';
-import 'package:neat_cache/cache_provider.dart';
-import 'package:neat_cache/neat_cache.dart';
-import 'package:mutex/mutex.dart';
-import 'package:retry/retry.dart';
-
-import 'logging.dart';
-
-/// Service for reading and writing values to a cache for quick access of data.
-///
-/// If [inMemory] is true, a cache with [inMemoryMaxNumberEntries] number
-/// of entries will be created. Otherwise, it will use the default redis cache.
-class CacheService {
- CacheService({
- bool inMemory = false,
- int inMemoryMaxNumberEntries = 256,
- }) : _provider =
- inMemory ? Cache.inMemoryCacheProvider(inMemoryMaxNumberEntries) : Cache.redisCacheProvider(memorystoreUri);
-
- final Mutex m = Mutex();
-
- final CacheProvider<List<int>> _provider;
-
- Cache<Uint8List> get cache => cacheValue ?? Cache<List<int>>(_provider).withCodec<Uint8List>(const _CacheCodec());
-
- @visibleForTesting
- Cache<Uint8List>? cacheValue;
-
- /// Google Cloud Memorystore default url.
- static Uri memorystoreUri = Uri.parse('redis://10.0.0.4:6379');
-
- /// An arbritary number for how many times we should try to get from cache
- /// before giving up.
- ///
- /// Writing to the cache creates a racy condition for when another operation
- /// is trying to get the same key. This race condition throws an exception.
- @visibleForTesting
- static const int maxCacheGetAttempts = 3;
-
- /// Get value of [key] from the subcache [subcacheName]. If the key has no
- /// value, call [createFn] to create a value for it, set it, and return it.
- ///
- /// The underlying cache get function is inherently racy as if there is a
- /// write operation while a read operation, getting the value can fail. To
- /// handle this racy condition, this attempts to get the value [maxCacheGetAttempts]
- /// times before giving up. This is because the cache is magnitudes faster
- /// than the fallback operation (usually a Datastore query).
- Future<Uint8List?> getOrCreate(
- String subcacheName,
- String key, {
- required Future<Uint8List> Function()? createFn,
- Duration ttl = const Duration(minutes: 1),
- }) async {
- Uint8List? value = await _readValue(subcacheName, key);
-
- // If given createFn, update the cache value if the value returned was null.
- if (createFn != null && value == null) {
- // Try creating the value
- value = await createFn();
- await set(
- subcacheName,
- key,
- value,
- ttl: ttl,
- );
- }
-
- return value;
- }
-
- /// This method is the same as the [getOrCreate] method above except that it
- /// enforces locking access.
- ///
- /// Note: these methods are intended to prevent issues around race conditions
- /// when storing and retrieving github tokens locally only for this instance.
- /// Care should be taken to use the locking methods together when accessing
- /// data from an entity using the cache.
- Future<Uint8List?> getOrCreateWithLocking(
- String subcacheName,
- String key, {
- required Future<Uint8List> Function()? createFn,
- Duration ttl = const Duration(minutes: 1),
- }) async {
- Uint8List? value = await _readValue(subcacheName, key);
-
- // If given createFn, update the cache value if the value returned was null.
- if (createFn != null && value == null) {
- // Try creating the value
- value = await createFn();
- await setWithLocking(
- subcacheName,
- key,
- value,
- ttl: ttl,
- );
- }
-
- return value;
- }
-
- Future<Uint8List?> _readValue(
- String subcacheName,
- String key,
- ) async {
- final Cache<Uint8List> subcache = cache.withPrefix(subcacheName);
- Uint8List? value;
-
- const RetryOptions r = RetryOptions(
- maxAttempts: maxCacheGetAttempts,
- delayFactor: Duration(milliseconds: 50),
- );
-
- try {
- await r.retry(
- () async {
- value = await subcache[key].get();
- },
- );
- } catch (e) {
- // If the last retry is unsuccessful on an exception we do not want to die
- // here.
- log.warning('Unable to retrieve value for $key from cache.');
- value = null;
- }
-
- return value;
- }
-
- /// Set [value] for [key] in the subcache [subcacheName] with [ttl].
- Future<Uint8List?> set(
- String subcacheName,
- String key,
- Uint8List? value, {
- Duration ttl = const Duration(minutes: 1),
- }) async {
- final Cache<Uint8List> subcache = cache.withPrefix(subcacheName);
- final Entry<Uint8List> entry = subcache[key];
- return entry.set(value, ttl);
- }
-
- /// Set [value] for [key] in the subcache [subcacheName] with [ttl] but
- /// enforce locking accessing.
- ///
- /// Note: these methods are intended to prevent issues around race conditions
- /// when storing and retrieving github tokens. Care should be taken to use the
- /// locking methods together when accessing data from an entity using the
- /// cache.
- Future<Uint8List?> setWithLocking(
- String subcacheName,
- String key,
- Uint8List? value, {
- Duration ttl = const Duration(minutes: 1),
- }) async {
- await m.acquire();
- try {
- return set(
- subcacheName,
- key,
- value,
- ttl: ttl,
- );
- } finally {
- m.release();
- }
- }
-
- /// Clear the value stored in subcache [subcacheName] for key [key].
- ///
- /// Note: these methods are intended to prevent issues around race conditions
- /// when storing and retrieving github tokens. Care should be taken to use the
- /// locking methods together when accessing data from an entity using the
- /// cache.
- Future<void> purge(String subcacheName, String key) async {
- await m.acquire();
- try {
- final Cache<Uint8List> subcache = cache.withPrefix(subcacheName);
- return subcache[key].purge(retries: maxCacheGetAttempts);
- } finally {
- m.release();
- }
- }
-
- void dispose() {
- _provider.close();
- }
-}
-
-class _CacheCodec extends Codec<Uint8List, List<int>> {
- const _CacheCodec();
-
- @override
- Converter<Uint8List, List<int>> get encoder => const _ListIntConverter();
-
- @override
- Converter<List<int>, Uint8List> get decoder => const _Uint8ListConverter();
-}
-
-class _ListIntConverter extends Converter<Uint8List, List<int>> {
- const _ListIntConverter();
-
- @override
- List<int> convert(Uint8List input) => input;
-}
-
-class _Uint8ListConverter extends Converter<List<int>, Uint8List> {
- const _Uint8ListConverter();
-
- @override
- Uint8List convert(List<int> input) => Uint8List.fromList(input);
-}
diff --git a/app_dart/lib/src/service/commit_service.dart b/app_dart/lib/src/service/commit_service.dart
deleted file mode 100644
index 61289da..0000000
--- a/app_dart/lib/src/service/commit_service.dart
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2021 The Flutter 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:async';
-
-import 'package:cocoon_service/src/model/appengine/commit.dart';
-import 'package:cocoon_service/src/service/config.dart';
-import 'package:cocoon_service/src/service/github_service.dart';
-import 'package:github/github.dart';
-import 'package:meta/meta.dart';
-import 'package:truncate/truncate.dart';
-
-import 'logging.dart';
-import 'package:cocoon_service/src/service/datastore.dart';
-import 'package:gcloud/db.dart';
-import 'package:github/hooks.dart';
-
-/// A class for doing various actions related to Github commits.
-class CommitService {
- CommitService({
- required this.config,
- @visibleForTesting this.datastoreProvider = DatastoreService.defaultProvider,
- });
-
- final Config config;
- final DatastoreServiceProvider datastoreProvider;
-
- /// Add a commit based on a [CreateEvent] to the Datastore.
- Future<void> handleCreateGithubRequest(CreateEvent createEvent) async {
- final DatastoreService datastore = datastoreProvider(config.db);
- final RepositorySlug slug = RepositorySlug.full(createEvent.repository!.fullName);
- final String branch = createEvent.ref!;
- log.info('Creating commit object for branch $branch in repository ${slug.fullName}');
- final Commit commit = await _createCommitFromBranchEvent(datastore, slug, branch);
- await _insertCommitIntoDatastore(datastore, commit);
- }
-
- /// Add a commit based on a Push event to the Datastore.
- /// https://docs.github.com/en/webhooks/webhook-events-and-payloads#push
- Future<void> handlePushGithubRequest(Map<String, dynamic> pushEvent) async {
- final DatastoreService datastore = datastoreProvider(config.db);
- final RepositorySlug slug = RepositorySlug.full(pushEvent['repository']['full_name']);
- final String sha = pushEvent['head_commit']['id'];
- final String branch = pushEvent['ref'].split('/')[2];
- final String id = '${slug.fullName}/$branch/$sha';
- final Key<String> key = datastore.db.emptyKey.append<String>(Commit, id: id);
- final Commit commit = Commit(
- key: key,
- timestamp: DateTime.parse(pushEvent['head_commit']['timestamp']).millisecondsSinceEpoch,
- repository: slug.fullName,
- sha: sha,
- author: pushEvent['sender']['login'],
- authorAvatarUrl: pushEvent['sender']['avatar_url'],
- // The field has a size of 1500 we need to ensure the commit message
- // is at most 1500 chars long.
- message: truncate(pushEvent['head_commit']['message'], 1490, omission: '...'),
- branch: branch,
- );
- await _insertCommitIntoDatastore(datastore, commit);
- }
-
- Future<Commit> _createCommitFromBranchEvent(DatastoreService datastore, RepositorySlug slug, String branch) async {
- final GithubService githubService = await config.createDefaultGitHubService();
- final GitReference gitRef = await githubService.getReference(slug, 'heads/$branch');
- final String sha = gitRef.object!.sha!;
- final RepositoryCommit commit = await githubService.github.repositories.getCommit(slug, sha);
-
- final String id = '${slug.fullName}/$branch/$sha';
- final Key<String> key = datastore.db.emptyKey.append<String>(Commit, id: id);
- return Commit(
- key: key,
- timestamp: DateTime.now().millisecondsSinceEpoch,
- repository: slug.fullName,
- sha: commit.sha,
- author: commit.author?.login,
- authorAvatarUrl: commit.author?.avatarUrl,
- // The field has a size of 1500 we need to ensure the commit message
- // is at most 1500 chars long.
- message: truncate(commit.commit!.message!, 1490, omission: '...'),
- branch: branch,
- );
- }
-
- Future<void> _insertCommitIntoDatastore(DatastoreService datastore, Commit commit) async {
- final DatastoreService datastore = datastoreProvider(config.db);
- try {
- log.info('Checking for existing commit in the datastore');
- await datastore.lookupByValue<Commit>(commit.key);
- } on KeyNotFoundException {
- log.info('Commit does not exist in datastore, inserting into datastore');
- await datastore.insert(<Commit>[commit]);
- }
- }
-}
diff --git a/app_dart/lib/src/service/config.dart b/app_dart/lib/src/service/config.dart
deleted file mode 100644
index f157443..0000000
--- a/app_dart/lib/src/service/config.dart
+++ /dev/null
@@ -1,443 +0,0 @@
-// Copyright 2019 The Flutter 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:convert';
-import 'dart:typed_data';
-
-import 'package:appengine/appengine.dart';
-import 'package:cocoon_service/src/service/datastore.dart';
-import 'package:corsac_jwt/corsac_jwt.dart';
-import 'package:gcloud/db.dart';
-import 'package:gcloud/service_scope.dart' as ss;
-import 'package:github/github.dart' as gh;
-import 'package:googleapis/bigquery/v2.dart';
-import 'package:graphql/client.dart';
-import 'package:http/http.dart' as http;
-import 'package:meta/meta.dart';
-import 'package:retry/retry.dart';
-
-import '../../cocoon_service.dart';
-import '../model/appengine/branch.dart';
-import '../model/appengine/cocoon_config.dart';
-import '../model/appengine/key_helper.dart';
-import 'access_client_provider.dart';
-import 'bigquery.dart';
-import 'github_service.dart';
-import 'logging.dart';
-
-/// Name of the default git branch.
-const String kDefaultBranchName = 'master';
-
-class Config {
- Config(this._db, this._cache);
-
- final DatastoreDB _db;
-
- final CacheService _cache;
-
- /// List of Github presubmit supported repos.
- ///
- /// This adds support for the `waiting for tree to go green label` to the repo.
- ///
- /// Relies on the GitHub Checks API being enabled for this repo.
- Set<gh.RepositorySlug> get supportedRepos => <gh.RepositorySlug>{
- cocoonSlug,
- engineSlug,
- flutterSlug,
- packagesSlug,
- };
-
- /// List of guaranteed scheduling Github repos.
- static Set<gh.RepositorySlug> get guaranteedSchedulingRepos => <gh.RepositorySlug>{
- engineSlug,
- packagesSlug,
- };
-
- /// List of Github postsubmit supported repos.
- ///
- /// This adds support for check runs to the repo.
- Set<gh.RepositorySlug> get postsubmitSupportedRepos => <gh.RepositorySlug>{
- packagesSlug,
- };
-
- /// GitHub repositories that use CI status to determine if pull requests can be submitted.
- static Set<gh.RepositorySlug> reposWithTreeStatus = <gh.RepositorySlug>{
- engineSlug,
- flutterSlug,
- };
-
- /// The tip of tree branch for [slug].
- static String defaultBranch(gh.RepositorySlug slug) {
- final Map<gh.RepositorySlug, String> defaultBranches = <gh.RepositorySlug, String>{
- cocoonSlug: 'main',
- flutterSlug: 'master',
- engineSlug: 'main',
- packagesSlug: 'main',
- recipesSlug: 'main',
- };
-
- return defaultBranches[slug] ?? kDefaultBranchName;
- }
-
- // The name of the bot that generates automated revert requests.
- String get autosubmitBot => 'auto-submit[bot]';
-
- // The name of the label that the bot uses to identify the automatically
- // created pull request.
- static const String revertOfLabel = 'revert of';
-
- /// Memorystore subcache name to store [CocoonConfig] values in.
- static const String configCacheName = 'config';
-
- /// Default properties when rerunning a prod build.
- static const Map<String, Object> defaultProperties = <String, Object>{'force_upload': true};
-
- @visibleForTesting
- static const Duration configCacheTtl = Duration(hours: 12);
-
- Logging get loggingService => ss.lookup(#appengine.logging) as Logging;
-
- Future<Iterable<Branch>> getBranches(gh.RepositorySlug slug) async {
- final DatastoreService datastore = DatastoreService(db, defaultMaxEntityGroups);
- final List<Branch> branches = await (datastore.queryBranches().toList());
-
- return branches.where((Branch branch) => branch.slug == slug);
- }
-
- Future<List<String>> _getReleaseAccounts() async {
- final String releaseAccountsConcat = await _getSingleValue('ReleaseAccounts');
- return releaseAccountsConcat.split(',');
- }
-
- Future<String> _getSingleValue(String id) async {
- final Uint8List? cacheValue = await _cache.getOrCreate(
- configCacheName,
- id,
- createFn: () => _getValueFromDatastore(id),
- ttl: configCacheTtl,
- );
-
- return String.fromCharCodes(cacheValue!);
- }
-
- Future<Uint8List> _getValueFromDatastore(String id) async {
- final CocoonConfig cocoonConfig = CocoonConfig()
- ..id = id
- ..parentKey = _db.emptyKey;
- final CocoonConfig result = await _db.lookupValue<CocoonConfig>(cocoonConfig.key);
-
- return Uint8List.fromList(result.value.codeUnits);
- }
-
- // GitHub App properties.
- Future<String> get githubPrivateKey => _getSingleValue('githubapp_private_pem');
- Future<String> get overrideTreeStatusLabel => _getSingleValue('override_tree_status_label');
- Future<String> get githubPublicKey => _getSingleValue('githubapp_public_pem');
- Future<String> get githubAppId => _getSingleValue('githubapp_id');
- Future<Map<String, dynamic>> get githubAppInstallations async {
- final String installations = await _getSingleValue('githubapp_installations');
- return jsonDecode(installations) as Map<String, dynamic>;
- }
-
- // Default recipe bundle used when the PR's base branch name does not exist in
- // the recipes GoB project.
- String get defaultRecipeBundleRef => 'refs/heads/main';
-
- DatastoreDB get db => _db;
-
- /// Size of the shards to send to buildBucket when scheduling builds.
- int get schedulingShardSize => 5;
-
- /// Batch size of builds to schedule in each swarming request.
- int get batchSize => 5;
-
- /// Upper limit of targets to be backfilled in API call.
- ///
- /// For example, if we have 200 available targets found to be backfilled,
- /// only `backfillerTargetLimit` will be scheduled whereas others wait for
- /// the next API call.
- int get backfillerTargetLimit => 50;
-
- /// Upper limit of commit rows to be backfilled in API call.
- ///
- /// This limits the number of commits to be checked to backfill. When bots
- /// are idle, we hope to scan as many commit rows as possible.
- int get backfillerCommitLimit => 50;
-
- /// Upper limit of issue/PRs allowed each API call.
- ///
- /// GitHub enforces a secondary rate limit on frequency API calls. This causes
- /// our API failure when many issues/PRs are created in a short time.
- int get issueAndPRLimit => 2;
-
- /// Max retries when scheduling builds.
- static const RetryOptions schedulerRetry = RetryOptions(maxAttempts: 3);
-
- /// List of GitHub accounts related to releases.
- Future<List<String>> get releaseAccounts => _getReleaseAccounts();
-
- Future<String> get oauthClientId => _getSingleValue('OAuthClientId');
-
- /// Webhook secret for the "Flutter Roll on Borg" GitHub App.
- Future<String> get frobWebhookKey => _getSingleValue('FrobWebhookKey');
-
- Future<String> get githubOAuthToken => _getSingleValue('GitHubPRToken');
-
- String get wrongBaseBranchPullRequestMessage => 'This pull request was opened against a branch other than '
- '_{{default_branch}}_. Since Flutter pull requests should not '
- 'normally be opened against branches other than {{default_branch}}, I '
- 'have changed the base to {{default_branch}}. If this was intended, you '
- 'may modify the base back to {{target_branch}}. See the [Release Process]'
- '(https://github.com/flutter/flutter/wiki/Release-process) for information '
- 'about how other branches get updated.\n\n'
- '__Reviewers__: Use caution before merging pull requests to branches other '
- 'than {{default_branch}}, unless this is an intentional hotfix/cherrypick.';
-
- String wrongHeadBranchPullRequestMessage(String branch) =>
- 'This pull request is trying merge the branch $branch, which is the name '
- 'of a release branch. This is usually a mistake. See '
- '[Tree Hygiene](https://github.com/flutter/flutter/wiki/Tree-hygiene) '
- 'for detailed instructions on how to contribute to the Flutter project. '
- 'In particular, ensure that before you start coding, you create your '
- 'feature branch off of _${kDefaultBranchName}_.\n\n'
- 'This PR has been closed. If you are sure you want to merge $branch, you '
- 'may re-open this issue.';
-
- String get releaseBranchPullRequestMessage => 'This pull request was opened '
- 'from and to a release candidate branch. This should only be done as part '
- 'of the official [Flutter release process]'
- '(https://github.com/flutter/flutter/wiki/Release-process). If you are '
- 'attempting to make a regular contribution to the Flutter project, please '
- 'close this PR and follow the instructions at [Tree Hygiene]'
- '(https://github.com/flutter/flutter/wiki/Tree-hygiene) for detailed '
- 'instructions on contributing to Flutter.\n\n'
- '__Reviewers__: Use caution before merging pull requests to release '
- 'branches. Ensure the proper procedure has been followed.';
-
- Future<String> get webhookKey => _getSingleValue('WebhookKey');
-
- String get mergeConflictPullRequestMessage => 'This pull request is not '
- 'mergeable in its current state, likely because of a merge conflict. '
- 'Pre-submit CI jobs were not triggered. Pushing a new commit to this '
- 'branch that resolves the issue will result in pre-submit jobs being '
- 'scheduled.';
-
- String get missingTestsPullRequestMessage => 'It looks like this pull '
- 'request may not have tests. Please make sure to add tests before merging. '
- 'If you need '
- '[an exemption](https://github.com/flutter/flutter/wiki/Tree-hygiene#tests) '
- 'to this rule, contact Hixie or stuartmorgan on the #hackers '
- 'channel in [Chat](https://github.com/flutter/flutter/wiki/Chat) '
- '(don\'t just cc them here, they won\'t see it! Use Discord!).'
- '\n\n'
- 'If you are not sure if you need tests, consider this rule of thumb: '
- 'the purpose of a test is to make sure someone doesn\'t accidentally '
- 'revert the fix. Ask yourself, **is there anything in your PR that you '
- 'feel it is important we not accidentally revert back to how it was '
- 'before your fix?**'
- '\n\n'
- '__Reviewers__: Read the [Tree Hygiene page]'
- '(https://github.com/flutter/flutter/wiki/Tree-hygiene#how-to-review-code) '
- 'and make sure this patch meets those guidelines before LGTMing.';
-
- String get flutterGoldPending => 'Waiting for all other checks to be successful before querying Gold.';
-
- String get flutterGoldSuccess => 'All golden file tests have passed.';
-
- String get flutterGoldChanges => 'Image changes have been found for '
- 'this pull request.';
-
- String get flutterGoldStalePR => 'This pull request executed golden file '
- 'tests, but it has not been updated in a while (20+ days). Test results from '
- 'Gold expire after as many days, so this pull request will need to be '
- 'updated with a fresh commit in order to get results from Gold.';
-
- String get flutterGoldDraftChange => 'This pull request has been changed to a '
- 'draft. The currently pending flutter-gold status will not be able '
- 'to resolve until a new commit is pushed or the change is marked ready for '
- 'review again.';
-
- String flutterGoldInitialAlert(String url) => 'Golden file changes have been found for this pull '
- 'request. Click [here to view and triage]($url) '
- '(e.g. because this is an intentional change).\n\n'
- 'If you are still iterating on this change and are not ready to '
- 'resolve the images on the Flutter Gold dashboard, consider marking this PR '
- 'as a draft pull request above. You will still be able to view image results '
- 'on the dashboard, commenting will be silenced, and the check will not try to resolve itself until '
- 'marked ready for review.\n\n';
-
- String flutterGoldFollowUpAlert(String url) => 'Golden file changes are available for triage from new commit, '
- 'Click [here to view]($url).\n\n';
-
- String flutterGoldAlertConstant(gh.RepositorySlug slug) {
- if (slug == Config.flutterSlug) {
- return '\n\nFor more guidance, visit '
- '[Writing a golden file test for `package:flutter`](https://github.com/flutter/flutter/wiki/Writing-a-golden-file-test-for-package:flutter).\n\n'
- '__Reviewers__: Read the [Tree Hygiene page](https://github.com/flutter/flutter/wiki/Tree-hygiene#how-to-review-code) '
- 'and make sure this patch meets those guidelines before LGTMing.\n\n';
- }
- return '';
- }
-
- String flutterGoldCommentID(gh.PullRequest pr) =>
- '_Changes reported for pull request #${pr.number} at sha ${pr.head!.sha}_\n\n';
-
- /// Post submit service account email used by LUCI swarming tasks.
- static const String luciProdAccount = 'flutter-prod-builder@chops-service-accounts.iam.gserviceaccount.com';
-
- /// Internal Google service account used to surface FRoB results.
- static const String frobAccount = 'flutter-roll-on-borg@flutter-roll-on-borg.google.com.iam.gserviceaccount.com';
-
- /// Service accounts used for PubSub messages.
- static const Set<String> allowedPubsubServiceAccounts = <String>{
- 'flutter-devicelab@flutter-dashboard.iam.gserviceaccount.com',
- 'flutter-dashboard@appspot.gserviceaccount.com',
- };
-
- int get maxTaskRetries => 2;
-
- /// Max retries for Luci builder with infra failure.
- int get maxLuciTaskRetries => 2;
-
- /// The default number of commit shown in flutter build dashboard.
- int get commitNumber => 30;
-
- KeyHelper get keyHelper => KeyHelper(applicationContext: context.applicationContext);
-
- // Default number of commits to return for benchmark dashboard.
- int /*!*/ get maxRecords => 50;
-
- // Delay between consecutive GitHub deflake request calls.
- Duration get githubRequestDelay => const Duration(seconds: 1);
-
- // Repository status context for github status.
- String get flutterBuild => 'flutter-build';
-
- // Repository status description for github status.
- String get flutterBuildDescription => 'Tree is currently broken. Please do not merge this '
- 'PR unless it contains a fix for the tree.';
-
- static gh.RepositorySlug get cocoonSlug => gh.RepositorySlug('flutter', 'cocoon');
- static gh.RepositorySlug get engineSlug => gh.RepositorySlug('flutter', 'engine');
- static gh.RepositorySlug get flutterSlug => gh.RepositorySlug('flutter', 'flutter');
- static gh.RepositorySlug get packagesSlug => gh.RepositorySlug('flutter', 'packages');
-
- /// Flutter recipes is hosted on Gerrit instead of GitHub.
- static gh.RepositorySlug get recipesSlug => gh.RepositorySlug('flutter', 'recipes');
-
- String get waitingForTreeToGoGreenLabelName => 'waiting for tree to go green';
-
- /// The names of autoroller accounts for the repositories.
- ///
- /// These accounts should not need reviews before merging. See
- /// https://github.com/flutter/flutter/wiki/Autorollers
- Set<String> get rollerAccounts => const <String>{
- 'skia-flutter-autoroll',
- 'engine-flutter-autoroll',
- 'dependabot',
- 'dependabot[bot]',
- };
-
- Future<String> generateJsonWebToken() async {
- final String privateKey = await githubPrivateKey;
- final String publicKey = await githubPublicKey;
- final JWTBuilder builder = JWTBuilder();
- final DateTime now = DateTime.now();
- builder
- ..issuer = await githubAppId
- ..issuedAt = now
- ..expiresAt = now.add(const Duration(minutes: 10));
- final JWTRsaSha256Signer signer = JWTRsaSha256Signer(privateKey: privateKey, publicKey: publicKey);
- final JWT signedToken = builder.getSignedToken(signer);
- return signedToken.toString();
- }
-
- Future<String> generateGithubToken(gh.RepositorySlug slug) async {
- // GitHub's secondary rate limits are run into very frequently when making auth tokens.
- final Uint8List? cacheValue = await _cache.getOrCreateWithLocking(
- configCacheName,
- 'githubToken-${slug.fullName}',
- createFn: () => _generateGithubToken(slug),
- // Tokens are minted for 10 minutes
- ttl: const Duration(minutes: 8),
- );
-
- return String.fromCharCodes(cacheValue!);
- }
-
- Future<Uint8List> _generateGithubToken(gh.RepositorySlug slug) async {
- final Map<String, dynamic> appInstallations = await githubAppInstallations;
- final String? appInstallation = appInstallations[slug.fullName]['installation_id'] as String?;
- final String jsonWebToken = await generateJsonWebToken();
- final Map<String, String> headers = <String, String>{
- 'Authorization': 'Bearer $jsonWebToken',
- 'Accept': 'application/vnd.github.machine-man-preview+json',
- };
- final Uri githubAccessTokensUri = Uri.https('api.github.com', 'app/installations/$appInstallation/access_tokens');
- final http.Response response = await http.post(githubAccessTokensUri, headers: headers);
- final Map<String, dynamic> jsonBody = jsonDecode(response.body) as Map<String, dynamic>;
- if (jsonBody.containsKey('token') == false) {
- log.warning(response.body);
- throw Exception('generateGitHubToken failed to get token from Github for repo=${slug.fullName}');
- }
- final String token = jsonBody['token'] as String;
- log.fine('Generated a new GitHub token for ${slug.fullName}');
- return Uint8List.fromList(token.codeUnits);
- }
-
- Future<gh.GitHub> createGitHubClient({gh.PullRequest? pullRequest, gh.RepositorySlug? slug}) async {
- slug ??= pullRequest!.base!.repo!.slug();
- final String githubToken = await generateGithubToken(slug);
- return createGitHubClientWithToken(githubToken);
- }
-
- gh.GitHub createGitHubClientWithToken(String token) {
- return gh.GitHub(auth: gh.Authentication.withToken(token));
- }
-
- Future<GraphQLClient> createGitHubGraphQLClient() async {
- final HttpLink httpLink = HttpLink(
- 'https://api.github.com/graphql',
- defaultHeaders: <String, String>{
- 'Accept': 'application/vnd.github.antiope-preview+json',
- },
- );
-
- final String token = await githubOAuthToken;
- final AuthLink authLink = AuthLink(
- getToken: () async => 'Bearer $token',
- );
-
- return GraphQLClient(
- cache: GraphQLCache(),
- link: authLink.concat(httpLink),
- );
- }
-
- Future<BigqueryService> createBigQueryService() async {
- final AccessClientProvider accessClientProvider = AccessClientProvider();
- return BigqueryService(accessClientProvider);
- }
-
- Future<TabledataResource> createTabledataResourceApi() async {
- return (await createBigQueryService()).defaultTabledata();
- }
-
- /// Default GitHub service when the repository does not matter.
- ///
- /// Internally uses the framework repo for OAuth.
- Future<GithubService> createDefaultGitHubService() async {
- return createGithubService(flutterSlug);
- }
-
- Future<GithubService> createGithubService(gh.RepositorySlug slug) async {
- final gh.GitHub github = await createGitHubClient(slug: slug);
- return GithubService(github);
- }
-
- GithubService createGithubServiceWithToken(String token) {
- final gh.GitHub github = createGitHubClientWithToken(token);
- return GithubService(github);
- }
-}
diff --git a/app_dart/lib/src/service/datastore.dart b/app_dart/lib/src/service/datastore.dart
deleted file mode 100644
index 73ece36..0000000
--- a/app_dart/lib/src/service/datastore.dart
+++ /dev/null
@@ -1,338 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-import 'dart:math';
-
-import 'package:cocoon_service/src/model/luci/buildbucket.dart';
-import 'package:cocoon_service/src/request_handling/exceptions.dart';
-import 'package:gcloud/datastore.dart' as gcloud_datastore;
-import 'package:gcloud/db.dart';
-import 'package:github/github.dart' show RepositorySlug, PullRequest;
-import 'package:grpc/grpc.dart';
-import 'package:meta/meta.dart';
-import 'package:retry/retry.dart';
-
-import '../model/appengine/branch.dart';
-import '../model/appengine/commit.dart';
-import '../model/appengine/github_build_status_update.dart';
-import '../model/appengine/github_gold_status_update.dart';
-import '../model/appengine/stage.dart';
-import '../model/appengine/task.dart';
-import '../service/logging.dart';
-import 'config.dart';
-
-/// Per the docs in [DatastoreDB.withTransaction], only 5 entity groups can
-/// be touched in any given transaction, or the backing datastore will throw
-/// an error.
-const int defaultMaxEntityGroups = 5;
-
-/// This number inherits from old GO backend, and is upto change if necessary.
-const int defaultTimeSeriesValuesNumber = 1500;
-
-/// Function signature for a [DatastoreService] provider.
-typedef DatastoreServiceProvider = DatastoreService Function(DatastoreDB db);
-
-/// Function signature that will be executed with retries.
-typedef RetryHandler = Function();
-
-/// Runs a db transaction with retries.
-///
-/// It uses quadratic backoff starting with 200ms and 3 max attempts.
-/// for context please read https://github.com/flutter/flutter/issues/54615.
-Future<void> runTransactionWithRetries(RetryHandler retryHandler, {RetryOptions? retryOptions}) {
- final RetryOptions r = retryOptions ??
- const RetryOptions(
- maxDelay: Duration(seconds: 10),
- maxAttempts: 3,
- );
- return r.retry(
- retryHandler,
- retryIf: (Exception e) => e is gcloud_datastore.TransactionAbortedError || e is GrpcError,
- );
-}
-
-/// Service class for interacting with App Engine cloud datastore.
-///
-/// This service exists to provide an API for common datastore queries made by
-/// the Cocoon backend.
-@immutable
-class DatastoreService {
- /// Creates a new [DatastoreService].
- ///
- /// The [db] argument must not be null.
- const DatastoreService(this.db, this.maxEntityGroups, {RetryOptions? retryOptions})
- : retryOptions = retryOptions ??
- const RetryOptions(
- maxDelay: Duration(seconds: 10),
- maxAttempts: 3,
- );
-
- /// Maximum number of entity groups to process at once.
- final int maxEntityGroups;
-
- /// The backing [DatastoreDB] object.
- final DatastoreDB db;
-
- /// Transaction retry configurations.
- final RetryOptions retryOptions;
-
- /// Creates and returns a [DatastoreService] using [db] and [maxEntityGroups].
- static DatastoreService defaultProvider(DatastoreDB db) {
- return DatastoreService(db, defaultMaxEntityGroups);
- }
-
- /// Queries for recent commits.
- ///
- /// The [limit] argument specifies the maximum number of commits to retrieve.
- ///
- /// The returned commits will be ordered by most recent [Commit.timestamp].
- Stream<Commit> queryRecentCommits({
- int limit = 100,
- int? timestamp,
- String? branch,
- required RepositorySlug slug,
- }) {
- timestamp ??= DateTime.now().millisecondsSinceEpoch;
- branch ??= Config.defaultBranch(slug);
- final Query<Commit> query = db.query<Commit>()
- ..limit(limit)
- ..filter('repository =', slug.fullName)
- ..filter('branch =', branch)
- ..order('-timestamp')
- ..filter('timestamp <', timestamp);
- return query.run();
- }
-
- Stream<Branch> queryBranches() {
- final Query<Branch> query = db.query<Branch>();
- return query.run();
- }
-
- /// Queries for recent [Task] by name.
- ///
- /// The [limit] argument specifies the maximum number of tasks to retrieve.
- ///
- /// The returned tasks will be ordered by most recent to oldest.
- Stream<Task> queryRecentTasksByName({
- int limit = 100,
- required String name,
- }) {
- final Query<Task> query = db.query<Task>()
- ..limit(limit)
- ..filter('name =', name)
- ..order('-createTimestamp');
- return query.run();
- }
-
- /// Queries for recent tasks that meet the specified criteria.
- ///
- /// Since each task belongs to a commit, this query implicitly includes a
- /// query of the most recent N commits (see [queryRecentCommits]). The
- /// [commitLimit] argument specifies how many commits to consider when
- /// retrieving the list of recent tasks.
- ///
- /// If [taskName] is specified, only tasks whose [Task.name] matches the
- /// specified value will be returned. By default, tasks will be returned
- /// regardless of their name.
- ///
- /// The returned tasks will be ordered by most recent [Commit.timestamp]
- /// first, then by most recent [Task.createTimestamp].
- Stream<FullTask> queryRecentTasks({
- String? taskName,
- int commitLimit = 20,
- String? branch,
- required RepositorySlug slug,
- }) async* {
- await for (Commit commit in queryRecentCommits(limit: commitLimit, branch: branch, slug: slug)) {
- final Query<Task> query = db.query<Task>(ancestorKey: commit.key)..order('-createTimestamp');
- if (taskName != null) {
- query.filter('name =', taskName);
- }
- yield* query.run().map<FullTask>((Task task) => FullTask(task, commit));
- }
- }
-
- /// Finds all tasks owned by the specified [commit] and partitions them into
- /// stages.
- ///
- /// The returned list of stages will be ordered by the natural ordering of
- /// [Stage].
- Future<List<Stage>> queryTasksGroupedByStage(Commit commit) async {
- final Query<Task> query = db.query<Task>(ancestorKey: commit.key)..order('-stageName');
- final Map<String?, StageBuilder> stages = <String?, StageBuilder>{};
- await for (Task task in query.run()) {
- if (!stages.containsKey(task.stageName)) {
- stages[task.stageName] = StageBuilder()
- ..commit = commit
- ..name = task.stageName;
- }
- stages[task.stageName]!.tasks.add(task);
- }
- final List<Stage> result = stages.values.map<Stage>((StageBuilder stage) => stage.build()).toList();
- return result..sort();
- }
-
- Future<GithubBuildStatusUpdate> queryLastStatusUpdate(
- RepositorySlug slug,
- PullRequest pr,
- ) async {
- final Query<GithubBuildStatusUpdate> query = db.query<GithubBuildStatusUpdate>()
- ..filter('repository =', slug.fullName)
- ..filter('pr =', pr.number)
- ..filter('head =', pr.head!.sha);
- final List<GithubBuildStatusUpdate> previousStatusUpdates = await query.run().toList();
-
- if (previousStatusUpdates.isEmpty) {
- return GithubBuildStatusUpdate(
- key: db.emptyKey.append<int>(GithubBuildStatusUpdate),
- repository: slug.fullName,
- pr: pr.number!,
- head: pr.head!.sha,
- updates: 0,
- updateTimeMillis: DateTime.now().millisecondsSinceEpoch,
- );
- } else {
- /// Duplicate cases rarely happen. It happens only when race condition
- /// occurs in app engine. When multiple records exist, the latest one
- /// is returned.
- if (previousStatusUpdates.length > 1) {
- return previousStatusUpdates.reduce(
- (GithubBuildStatusUpdate current, GithubBuildStatusUpdate next) =>
- current.updateTimeMillis! < next.updateTimeMillis! ? next : current,
- );
- }
- return previousStatusUpdates.single;
- }
- }
-
- Future<GithubGoldStatusUpdate> queryLastGoldUpdate(
- RepositorySlug slug,
- PullRequest pr,
- ) async {
- final Query<GithubGoldStatusUpdate> query = db.query<GithubGoldStatusUpdate>()
- ..filter('repository =', slug.fullName)
- ..filter('pr =', pr.number);
- final List<GithubGoldStatusUpdate> previousStatusUpdates = await query.run().toList();
-
- if (previousStatusUpdates.isEmpty) {
- return GithubGoldStatusUpdate(
- pr: pr.number!,
- head: '',
- status: '',
- updates: 0,
- description: '',
- repository: slug.fullName,
- );
- } else {
- if (previousStatusUpdates.length > 1) {
- throw StateError('GithubGoldStatusUpdate should have no more than one entry on '
- 'repository ${slug.fullName}, pr ${pr.number}.');
- }
- return previousStatusUpdates.single;
- }
- }
-
- /// Shards [rows] into several sublists of size [maxEntityGroups].
- Future<List<List<Model<dynamic>>>> shard(List<Model<dynamic>> rows) async {
- final List<List<Model<dynamic>>> shards = <List<Model<dynamic>>>[];
- for (int i = 0; i < rows.length; i += maxEntityGroups) {
- shards.add(rows.sublist(i, i + min<int>(rows.length - i, maxEntityGroups)));
- }
- return shards;
- }
-
- /// Inserts [rows] into datastore sharding the inserts if needed.
- Future<void> insert(List<Model<dynamic>> rows) async {
- final List<List<Model<dynamic>>> shards = await shard(rows);
- for (List<Model<dynamic>> shard in shards) {
- await runTransactionWithRetries(
- () async {
- await db.withTransaction<void>((Transaction transaction) async {
- transaction.queueMutations(inserts: shard);
- await transaction.commit();
- });
- },
- retryOptions: retryOptions,
- );
- }
- }
-
- /// Looks up registers by [keys].
- Future<List<T?>> lookupByKey<T extends Model<dynamic>>(List<Key<dynamic>> keys) async {
- List<T?> results = <T>[];
- await runTransactionWithRetries(
- () async {
- await db.withTransaction<void>((Transaction transaction) async {
- results = await transaction.lookup<T>(keys);
- await transaction.commit();
- });
- },
- retryOptions: retryOptions,
- );
- return results;
- }
-
- /// Looks up registers by value using a single [key].
- Future<T> lookupByValue<T extends Model<dynamic>>(Key<dynamic> key, {T Function()? orElse}) async {
- late T result;
- await runTransactionWithRetries(
- () async {
- await db.withTransaction<void>((Transaction transaction) async {
- result = await db.lookupValue<T>(key, orElse: orElse);
- await transaction.commit();
- });
- },
- retryOptions: retryOptions,
- );
- return result;
- }
-
- /// Runs a function inside a transaction providing a [Transaction] parameter.
- Future<T?> withTransaction<T>(Future<T> Function(Transaction) handler) async {
- T? result;
- await runTransactionWithRetries(
- () async {
- await db.withTransaction<void>((Transaction transaction) async {
- result = await handler(transaction);
- });
- },
- retryOptions: retryOptions,
- );
- return result;
- }
-
- Future<Task?> getTaskFromBuildbucketBuild(
- Build build, {
- String? customName,
- }) async {
- log.fine('Generating commit key from buildbucket build: ${build.toString()}');
-
- final String repository = build.input!.gitilesCommit!.project!.split('/')[1];
- log.fine('Repository: $repository');
-
- final String branch = build.input!.gitilesCommit!.ref!.split('/')[2];
- log.fine('Branch: $branch');
-
- final String hash = build.input!.gitilesCommit!.hash!;
- log.fine('Hash: $hash');
-
- final RepositorySlug slug = RepositorySlug('flutter', repository);
- log.fine('Slug: ${slug.toString()}');
-
- final String id = '${slug.fullName}/$branch/$hash';
- final Key<String> commitKey = db.emptyKey.append<String>(Commit, id: id);
-
- try {
- return await Task.fromDatastore(
- datastore: this,
- commitKey: commitKey,
- name: customName ?? build.builderId.builder,
- );
- } on InternalServerError catch (e) {
- log.warning('Failed to find an existing task for the buildbucket build: ${e.toString()}');
- return null;
- }
- }
-}
diff --git a/app_dart/lib/src/service/exceptions.dart b/app_dart/lib/src/service/exceptions.dart
deleted file mode 100644
index eec4c17..0000000
--- a/app_dart/lib/src/service/exceptions.dart
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2022 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-class NoBuildFoundException implements Exception {
- /// Create a custom exception for no build found Errors.
- NoBuildFoundException(this.cause);
-
- final String cause;
-
- @override
- String toString() => cause;
-}
-
-class UnfinishedBuildException implements Exception {
- /// Create a custom exception for an unfinished buildbucket build
- UnfinishedBuildException(this.cause);
-
- final String cause;
-
- @override
- String toString() => cause;
-}
diff --git a/app_dart/lib/src/service/gerrit_service.dart b/app_dart/lib/src/service/gerrit_service.dart
deleted file mode 100644
index ac9cd42..0000000
--- a/app_dart/lib/src/service/gerrit_service.dart
+++ /dev/null
@@ -1,180 +0,0 @@
-// Copyright 2020 The Flutter 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:async';
-import 'dart:convert';
-import 'dart:io';
-
-import 'package:cocoon_service/src/request_handling/exceptions.dart';
-import 'package:github/github.dart';
-import 'package:googleapis_auth/auth_io.dart';
-import 'package:http/http.dart' as http;
-import 'package:http/retry.dart';
-import 'package:meta/meta.dart';
-
-import '../model/gerrit/commit.dart';
-import 'config.dart';
-import 'logging.dart';
-
-/// Communicates with gerrit APIs https://gerrit-review.googlesource.com/Documentation/rest-api-projects.html
-/// to get information about projects hosted in Git on Borg.
-class GerritService {
- GerritService({
- required this.config,
- http.Client? httpClient,
- @visibleForTesting this.authClientProvider = clientViaApplicationDefaultCredentials,
- @visibleForTesting this.retryDelay,
- }) : httpClient = httpClient ?? http.Client();
-
- final Config config;
- final http.Client httpClient;
-
- final Duration? retryDelay;
-
- /// Provider for generating a [http.Client] that is authenticated to make calls to GCP services.
- final Future<AutoRefreshingAuthClient> Function({
- http.Client? baseClient,
- required List<String> scopes,
- }) authClientProvider;
-
- /// Gets the branches from a remote git repository using the gerrit APIs.
- ///
- /// [filterRegex] a regular expression string to filter the branches list to
- /// the ones matching the regex.
- ///
- /// See more:
- /// * https://gerrit-review.googlesource.com/Documentation/rest-api-projects.html#list-branches
- Future<List<String>> branches(String repo, String project, {String? filterRegex}) async {
- final Map<String, String> queryParameters = <String, String>{
- if (filterRegex != null && filterRegex.isNotEmpty) 'r': filterRegex,
- };
- final Uri url = Uri.https(repo, 'projects/$project/branches', queryParameters);
- final List<dynamic> response = await _getJson(url) as List<dynamic>;
-
- final List<String> branches = <String>[];
-
- final Iterable<Map<String, dynamic>> json = response.map((dynamic e) => e as Map<String, dynamic>);
- for (Map<String, dynamic> element in json) {
- branches.add(element['ref'] as String);
- }
-
- return branches;
- }
-
- /// Gets the commit log for a project-branch pair.
- Future<Iterable<GerritCommit>> commits(RepositorySlug slug, String branch) async {
- final Uri url =
- Uri.https('${slug.owner}.googlesource.com', '${slug.name}/+log/refs/heads/$branch', <String, String>{
- 'format': 'json',
- });
- final Map<String, dynamic> response = await _getJson(url) as Map<String, dynamic>;
- final List<dynamic> commitsJson = response['log'] as List<dynamic>;
-
- return commitsJson.map((dynamic part) => GerritCommit.fromJson(part as Map<String, dynamic>));
- }
-
- /// Finds a commit on a GoB mirror using the GitHub [slug] and commit [sha].
- ///
- /// The [slug] will be validated by checking if it represents a presubmit or postsubmit supported repo.
- Future<GerritCommit?> findMirroredCommit(RepositorySlug slug, String sha) async {
- if (!config.supportedRepos.contains(slug)) return null;
- final gobMirrorName = 'mirrors/${slug.name}';
- return getCommit(RepositorySlug(slug.owner, gobMirrorName), sha);
- }
-
- /// Gets the commit info from Gob.
- ///
- /// See more:
- /// * https://gerrit-review.googlesource.com/Documentation/rest-api-projects.html#get-commit
- Future<GerritCommit?> getCommit(RepositorySlug slug, String commitId) async {
- // URL encode because "mirrors/flutter" should be "mirrors%2Fflutter".
- final String projectName = Uri.encodeComponent(slug.name);
- // Uses [Uri] constructor instead of [Uri.https], where the latter uses [unencodedPath].
- // Our mirror repo, say [mirrors/cocoon], will not be encoded
- // as [mirrors%2Fcocoon] if injected directly. On the other hand, if we inject an encoded
- // version [mirrors%2Fcocoon], [Uri.https] will translate that to [mirrors%252Fcocoon].
- // Neither works with [Uri.https].
- final Uri url = Uri(
- scheme: 'https',
- host: '${slug.owner}-review.googlesource.com',
- path: 'projects/$projectName/commits/$commitId',
- );
- log.info('Gerrit get-commit url: $url');
-
- final http.Response response = await _get(url);
- log.info('Gob commit response for commit $commitId: ${response.body}');
- if (!_responseIsAcceptable(response)) return null;
-
- final String jsonBody = _stripXssToken(response.body);
- final Map<String, dynamic> json = jsonDecode(jsonBody) as Map<String, dynamic>;
- return GerritCommit.fromJson(json);
- }
-
- /// Strips magic prefix from response body.
- ///
- /// To prevent against Cross Site Script Inclusion (XSSI) attacks, the JSON response body starts with a magic prefix line that
- /// must be stripped before feeding the rest of the response body to a JSON parser. The magic prefix is ")]}'".
- String _stripXssToken(String body) {
- return body.replaceRange(0, 4, '');
- }
-
- /// Creates a new branch.
- ///
- /// See more:
- /// * https://gerrit-review.googlesource.com/Documentation/rest-api-projects.html#create-branch
- Future<void> createBranch(RepositorySlug slug, String branchName, String revision) async {
- log.info('Creating branch $branchName at $revision');
- final Uri url = Uri.https('${slug.owner}-review.googlesource.com', 'projects/${slug.name}/branches/$branchName');
- final Map<String, dynamic> response = await _put(
- url,
- body: revision,
- ) as Map<String, dynamic>;
- log.info(response);
- if (response['revision'] != revision) {
- throw const InternalServerError('Failed to create branch');
- }
- log.info('Created branch $branchName');
- }
-
- Future<dynamic> _getJson(Uri url) async {
- final RetryClient client = RetryClient(httpClient);
- final http.Response response = await client.get(url);
- final String jsonBody = _stripXssToken(response.body);
- return jsonDecode(jsonBody) as dynamic;
- }
-
- Future<http.Response> _get(Uri url) async {
- final RetryClient client = RetryClient(httpClient);
- return client.get(url);
- }
-
- Future<dynamic> _put(
- Uri url, {
- Object? body,
- }) async {
- final http.Client authClient = await authClientProvider(baseClient: httpClient, scopes: <String>[]);
- // GoB replicas may not have all the Flutter state, and can require several retries
- final http.Client client = RetryClient(
- authClient,
- when: (http.BaseResponse response) => _responseIsAcceptable(response) == false,
- delay: (int attempt) => retryDelay ?? const Duration(seconds: 1) * attempt,
- );
- final http.Response response = await client.put(
- url,
- body: body,
- );
- if (_responseIsAcceptable(response) == false) {
- throw InternalServerError('Gerrit returned ${response.statusCode} which is not 200 or 202');
- }
- log.info('Sent PUT to $url');
- log.info(response.body);
- // Remove XSS token
- final String jsonBody = _stripXssToken(response.body);
- log.info(jsonBody);
- return jsonDecode(jsonBody);
- }
-
- bool _responseIsAcceptable(http.BaseResponse response) =>
- response.statusCode == HttpStatus.ok || response.statusCode == HttpStatus.accepted;
-}
diff --git a/app_dart/lib/src/service/github_checks_service.dart b/app_dart/lib/src/service/github_checks_service.dart
deleted file mode 100644
index 9a962e2..0000000
--- a/app_dart/lib/src/service/github_checks_service.dart
+++ /dev/null
@@ -1,238 +0,0 @@
-// Copyright 2020 The Flutter 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 'package:github/github.dart' as github;
-import 'package:github/hooks.dart';
-
-import '../foundation/github_checks_util.dart';
-import '../model/luci/buildbucket.dart';
-import '../model/luci/push_message.dart' as push_message;
-import 'config.dart';
-import 'github_service.dart';
-import 'logging.dart';
-import 'luci_build_service.dart';
-import 'scheduler.dart';
-
-const String kGithubSummary = '''
-**[Understanding a LUCI build failure](https://github.com/flutter/flutter/wiki/Understanding-a-LUCI-build-failure)**
-
-''';
-
-/// Controls triggering builds and updating their status in the Github UI.
-class GithubChecksService {
- GithubChecksService(this.config, {GithubChecksUtil? githubChecksUtil})
- : githubChecksUtil = githubChecksUtil ?? const GithubChecksUtil();
-
- Config config;
- GithubChecksUtil githubChecksUtil;
-
- static Set<github.CheckRunConclusion> failedStatesSet = <github.CheckRunConclusion>{
- github.CheckRunConclusion.cancelled,
- github.CheckRunConclusion.failure,
- };
-
- /// Takes a [CheckSuiteEvent] and trigger all the relevant builds if this is a
- /// new commit or only failed builds if the event was generated by a click on
- /// the re-run all button in the Github UI.
- /// Relevant API docs:
- /// https://docs.github.com/en/rest/reference/checks#create-a-check-suite
- /// https://docs.github.com/en/rest/reference/checks#rerequest-a-check-suite
- Future<void> handleCheckSuite(
- github.PullRequest pullRequest,
- CheckSuiteEvent checkSuiteEvent,
- Scheduler scheduler,
- ) async {
- switch (checkSuiteEvent.action) {
- case 'requested':
- // Trigger all try builders.
- log.info('Check suite request for pull request ${pullRequest.number}, ${pullRequest.title}');
- await scheduler.triggerPresubmitTargets(
- pullRequest: pullRequest,
- );
- break;
- case 'rerequested':
- log.info('Check suite re-request for pull request ${pullRequest.number}, ${pullRequest.title}');
- pullRequest.head = github.PullRequestHead(sha: checkSuiteEvent.checkSuite?.headSha);
- return scheduler.retryPresubmitTargets(
- pullRequest: pullRequest,
- checkSuiteEvent: checkSuiteEvent,
- );
- }
- }
-
- /// Updates the Github build status using a [BuildPushMessage] sent by LUCI in
- /// a pub/sub notification.
- /// Relevant APIs:
- /// https://docs.github.com/en/rest/reference/checks#update-a-check-run
- Future<bool> updateCheckStatus(
- push_message.BuildPushMessage buildPushMessage,
- LuciBuildService luciBuildService,
- github.RepositorySlug slug, {
- bool rescheduled = false,
- }) async {
- final push_message.Build? build = buildPushMessage.build;
- if (buildPushMessage.userData.isEmpty) {
- return false;
- }
-
- if (!buildPushMessage.userData.containsKey('check_run_id') ||
- !buildPushMessage.userData.containsKey('repo_owner') ||
- !buildPushMessage.userData.containsKey('repo_name')) {
- log.severe(
- 'UserData did not contain check_run_id,'
- 'repo_owner, or repo_name: ${buildPushMessage.userData}',
- );
- return false;
- }
- github.CheckRunStatus status = statusForResult(build!.status);
- // Only `id` and `name` in the CheckRun are needed.
- // Instead of making an API call to get the details of each check run, we
- // generate the check run with only necessary info.
- final github.CheckRun checkRun = github.CheckRun.fromJson({
- 'id': buildPushMessage.userData['check_run_id'] as int?,
- 'status': status,
- 'check_suite': const {'id': null},
- 'started_at': build.createdTimestamp.toString(),
- 'conclusion': null,
- 'name': build.buildParameters!['builder_name'],
- });
- github.CheckRunConclusion? conclusion =
- (buildPushMessage.build!.result != null) ? conclusionForResult(buildPushMessage.build!.result) : null;
- final String? url = buildPushMessage.build!.url;
- github.CheckRunOutput? output;
- // If status has completed with failure then provide more details.
- if (taskFailed(buildPushMessage)) {
- if (rescheduled) {
- status = github.CheckRunStatus.queued;
- conclusion = null;
- output = github.CheckRunOutput(
- title: checkRun.name!,
- summary: 'Note: this is an auto rerun. The timestamp above is based on the first attempt of this check run.',
- );
- } else {
- final Build buildbucketBuild =
- await luciBuildService.getBuildById(buildPushMessage.build!.id, fields: 'id,builder,summaryMarkdown');
- output = github.CheckRunOutput(
- title: checkRun.name!,
- summary: getGithubSummary(buildbucketBuild.summaryMarkdown),
- );
- log.fine('Updating check run with output: [$output]');
- }
- }
- await githubChecksUtil.updateCheckRun(
- config,
- slug,
- checkRun,
- status: status,
- conclusion: conclusion,
- detailsUrl: url,
- output: output,
- );
- return true;
- }
-
- /// Check if task has completed with failure.
- bool taskFailed(push_message.BuildPushMessage buildPushMessage) {
- final push_message.Build? build = buildPushMessage.build;
- final github.CheckRunStatus status = statusForResult(build!.status);
- final github.CheckRunConclusion? conclusion =
- (buildPushMessage.build!.result != null) ? conclusionForResult(buildPushMessage.build!.result) : null;
- return status == github.CheckRunStatus.completed && failedStatesSet.contains(conclusion);
- }
-
- /// Returns current reschedule attempt.
- ///
- /// It returns 1 if this is the first run, and +1 with each reschedule.
- int currentAttempt(push_message.BuildPushMessage buildPushMessage) {
- final push_message.Build build = buildPushMessage.build!;
- if (build.tagsByName('current_attempt').isEmpty) {
- return 1;
- } else {
- return int.parse(build.tagsByName('current_attempt').single);
- }
- }
-
- /// Appends triage wiki page to `summaryMarkdown` from LUCI build so that people can easily
- /// reference from github check run page.
- String getGithubSummary(String? summary) {
- if (summary == null) {
- return '${kGithubSummary}Empty summaryMarkdown';
- }
- // This is an imposed GitHub limit
- const int checkSummaryLimit = 65535;
- // This is to give buffer room incase GitHub lowers the amount.
- const int checkSummaryBufferLimit = checkSummaryLimit - 10000 - kGithubSummary.length;
- // Return the last [checkSummaryBufferLimit] characters as they are likely the most relevant.
- if (summary.length > checkSummaryBufferLimit) {
- final String truncatedSummary = summary.substring(summary.length - checkSummaryBufferLimit);
- summary = '[TRUNCATED...] $truncatedSummary';
- }
- return '$kGithubSummary$summary';
- }
-
- /// Transforms a [push_message.Result] to a [github.CheckRunConclusion].
- /// Relevant APIs:
- /// https://developer.github.com/v3/checks/runs/#check-runs
- github.CheckRunConclusion conclusionForResult(push_message.Result? result) {
- switch (result) {
- case push_message.Result.canceled:
- // Set conclusion cancelled as a failure to ensure developers can retry
- // tasks when builds timeout.
- return github.CheckRunConclusion.failure;
- case push_message.Result.failure:
- return github.CheckRunConclusion.failure;
- case push_message.Result.success:
- return github.CheckRunConclusion.success;
- case null:
- throw StateError('unreachable');
- }
- }
-
- /// Transforms a [push_message.Status] to a [github.CheckRunStatus].
- /// Relevant APIs:
- /// https://developer.github.com/v3/checks/runs/#check-runs
- github.CheckRunStatus statusForResult(push_message.Status? status) {
- switch (status) {
- case push_message.Status.completed:
- return github.CheckRunStatus.completed;
- case push_message.Status.scheduled:
- return github.CheckRunStatus.queued;
- case push_message.Status.started:
- return github.CheckRunStatus.inProgress;
- case null:
- throw StateError('unreachable');
- }
- }
-
- /// Given a [headSha] and [checkSuiteId], finds the [PullRequest] that matches.
- Future<github.PullRequest?> findMatchingPullRequest(
- github.RepositorySlug slug,
- String headSha,
- int checkSuiteId,
- ) async {
- final GithubService githubService = await config.createDefaultGitHubService();
-
- // There could be multiple PRs that have the same [headSha] commit.
- final List<github.Issue> prIssues = await githubService.searchIssuesAndPRs(slug, '$headSha type:pr');
-
- for (final prIssue in prIssues) {
- final int prNumber = prIssue.number;
-
- // Each PR can have multiple check suites.
- final List<github.CheckSuite> checkSuites = await githubChecksUtil.listCheckSuitesForRef(
- githubService.github,
- slug,
- ref: 'refs/pull/$prNumber/head',
- );
-
- // Use check suite ID equality to verify that we have iterated to the correct PR.
- final bool doesPrIncludeMatchingCheckSuite = checkSuites.any((checkSuite) => checkSuite.id! == checkSuiteId);
- if (doesPrIncludeMatchingCheckSuite) {
- return githubService.getPullRequest(slug, prNumber);
- }
- }
-
- return null;
- }
-}
diff --git a/app_dart/lib/src/service/github_service.dart b/app_dart/lib/src/service/github_service.dart
deleted file mode 100644
index 19d605e..0000000
--- a/app_dart/lib/src/service/github_service.dart
+++ /dev/null
@@ -1,336 +0,0 @@
-// Copyright 2019 The Flutter 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:convert';
-import 'dart:math';
-
-import 'package:github/github.dart';
-import 'package:http/http.dart';
-
-import '../service/logging.dart';
-
-class GithubService {
- GithubService(this.github);
-
- final GitHub github;
- static final Map<String, String> headers = <String, String>{'Accept': 'application/vnd.github.groot-preview+json'};
- static const String kRefsPrefix = 'refs/heads/';
-
- /// Return commits unique to [branch] for the repository [slug].
- ///
- /// When [lastCommitTimestampMills] equals 0, it means a new release branch is
- /// found and only the branched commit will be returned for now, though the
- /// rare case that multiple commits exist. For other cases, it returns all
- /// newer commits since [lastCommitTimestampMills].
- Future<List<RepositoryCommit>> listBranchedCommits(
- RepositorySlug slug,
- String branch,
- int? lastCommitTimestampMills,
- ) async {
- ArgumentError.checkNotNull(slug);
- final PaginationHelper paginationHelper = PaginationHelper(github);
-
- /// The [pages] defines the number of pages of returned http request
- /// results. Return only one page when this is a new branch. Otherwise
- /// it will return all commits prior to this release branch commit,
- /// leading to heavy workload.
- int? pages;
- if (lastCommitTimestampMills == null || lastCommitTimestampMills == 0) {
- pages = 1;
- }
-
- List<Map<String, dynamic>> commits = <Map<String, dynamic>>[];
-
- /// [lastCommitTimestamp+1] excludes last commit itself.
- /// Github api url: https://developer.github.com/v3/repos/commits/#list-commits
- await for (Response response in paginationHelper.fetchStreamed(
- 'GET',
- '/repos/${slug.fullName}/commits',
- params: <String, dynamic>{
- 'sha': branch,
- 'since': DateTime.fromMillisecondsSinceEpoch((lastCommitTimestampMills ?? 0) + 1).toUtc().toIso8601String(),
- },
- pages: pages,
- headers: headers,
- )) {
- commits.addAll((json.decode(response.body) as List<dynamic>).cast<Map<String, dynamic>>());
- }
-
- /// When a release branch is first detected only the most recent commit would be needed.
- ///
- /// If for the worst case, a new release branch consists of a useful cherry pick commit
- /// which should be considered as well, here is the todo.
- // TODO(keyonghan): https://github.com/flutter/flutter/issues/59275
- if (lastCommitTimestampMills == 0) {
- commits = commits.take(1).toList();
- }
-
- return commits.map<RepositoryCommit>((Map<String, dynamic> commit) {
- return RepositoryCommit()
- ..sha = commit['sha'] as String?
- ..author = (User()
- ..login = commit['author']['login'] as String?
- ..avatarUrl = commit['author']['avatar_url'] as String?)
- ..commit = (GitCommit()
- ..message = commit['commit']['message'] as String?
- ..committer = (GitCommitUser(
- commit['commit']['author']['name'] as String?,
- commit['commit']['author']['email'] as String?,
- DateTime.parse(commit['commit']['author']['date'] as String),
- )));
- }).toList();
- }
-
- /// List pull requests in the repository.
- Future<List<PullRequest>> listPullRequests(RepositorySlug slug, String? branch) {
- ArgumentError.checkNotNull(slug);
- return github.pullRequests
- .list(
- slug,
- base: branch,
- direction: 'desc',
- sort: 'created',
- state: 'open',
- )
- .toList();
- }
-
- /// Creates a pull request against the `baseRef` in the `slug` repository.
- ///
- /// The `entries` contains the file changes in the created pull request. This
- /// method creates a branch in the current user's forked repository to create
- /// the pull request. The current user must have a forked repository from the
- /// targeted slug, and the targeted slug must not be belong to current user.
- Future<PullRequest> createPullRequest(
- RepositorySlug slug, {
- required String title,
- String? body,
- String? commitMessage,
- required GitReference baseRef,
- List<CreateGitTreeEntry>? entries,
- }) async {
- ArgumentError.checkNotNull(slug);
- ArgumentError.checkNotNull(title);
-
- final RepositorySlug clientSlug = await _getCurrentUserSlug(slug.name);
- final GitTree tree = await github.git.createTree(clientSlug, CreateGitTree(entries, baseTree: baseRef.object!.sha));
- final CurrentUser currentUser = (await _getCurrentUser())!;
- final GitCommitUser commitUser = GitCommitUser(currentUser.name, currentUser.email, DateTime.now());
- final GitCommit commit = await github.git.createCommit(
- clientSlug,
- CreateGitCommit(
- commitMessage,
- tree.sha,
- parents: <String?>[baseRef.object!.sha],
- author: commitUser,
- committer: commitUser,
- ),
- );
- final GitReference headRef =
- await github.git.createReference(clientSlug, '$kRefsPrefix${_generateNewRef()}', commit.sha);
- return github.pullRequests.create(
- slug,
- CreatePullRequest(title, '${clientSlug.owner}:${headRef.ref}', baseRef.ref, body: body),
- );
- }
-
- /// Assigns a reviewer to the pull request in the repository.
- ///
- /// The `reviewer` contains the github login of the reviewer.
- Future<void> assignReviewer(
- RepositorySlug slug, {
- int? pullRequestNumber,
- String? reviewer,
- }) async {
- const JsonEncoder encoder = JsonEncoder();
- await github.postJSON<Map<String, dynamic>, PullRequest>(
- '/repos/${slug.fullName}/pulls/$pullRequestNumber/requested_reviewers',
- convert: (Map<String, dynamic> i) => PullRequest.fromJson(i),
- body: encoder.convert(<String, dynamic>{
- 'reviewers': <String?>[reviewer],
- }),
- );
- }
-
- /// Adds labels to an issue.
- ///
- /// A pull request is an issue. This works for pull requests as well.
- Future<List<IssueLabel>> addIssueLabels(
- RepositorySlug slug,
- int issueNumber,
- List<String> labels,
- ) async {
- ArgumentError.checkNotNull(slug);
- ArgumentError.checkNotNull(issueNumber);
- ArgumentError.checkNotNull(labels);
- return github.issues.addLabelsToIssue(slug, issueNumber, labels);
- }
-
- /// Retrieves issues from the repository.
- ///
- /// Uses the `labels` to return the issues that have the labels.
- ///
- /// The `state` can be set `open`, `closed, or `all`. If it is set to `open`,
- /// this method only returns issues that are currently open. If it is set to
- /// `closed`, this method returns issues that are currently closed. The `all`
- /// returns both closed and open issues. Defaults to `open`.
- Future<List<Issue>> listIssues(
- RepositorySlug slug, {
- List<String>? labels,
- String state = 'open',
- }) {
- ArgumentError.checkNotNull(slug);
- return github.issues.listByRepo(slug, labels: labels, state: state).toList();
- }
-
- /// Get an issue with the issue number
- Future<Issue>? getIssue(
- RepositorySlug slug, {
- required int issueNumber,
- }) {
- ArgumentError.checkNotNull(slug);
- ArgumentError.checkNotNull(issueNumber);
- return github.issues.get(slug, issueNumber);
- }
-
- /// Assign the issue to the assignee.
- Future<void> assignIssue(
- RepositorySlug slug, {
- required int issueNumber,
- required String assignee,
- }) async {
- ArgumentError.checkNotNull(slug);
- ArgumentError.checkNotNull(issueNumber);
- ArgumentError.checkNotNull(assignee);
- await github.issues.edit(slug, issueNumber, IssueRequest(assignee: assignee));
- }
-
- Future<Issue> createIssue(
- RepositorySlug slug, {
- String? title,
- String? body,
- List<String>? labels,
- String? assignee,
- }) async {
- ArgumentError.checkNotNull(slug);
- return github.issues.create(
- slug,
- IssueRequest(title: title, body: body, labels: labels, assignee: assignee),
- );
- }
-
- Future<IssueComment?> createComment(
- RepositorySlug slug, {
- required int issueNumber,
- required String body,
- }) async {
- ArgumentError.checkNotNull(slug);
- ArgumentError.checkNotNull(issueNumber);
- return github.issues.createComment(slug, issueNumber, body);
- }
-
- Future<List<IssueLabel>> replaceLabelsForIssue(
- RepositorySlug slug, {
- required int issueNumber,
- required List<String> labels,
- }) async {
- ArgumentError.checkNotNull(slug);
- ArgumentError.checkNotNull(issueNumber);
- final Response response = await github.request(
- 'PUT',
- '/repos/${slug.fullName}/issues/$issueNumber/labels',
- body: GitHubJson.encode(labels),
- );
- final List<dynamic> body = jsonDecode(response.body) as List<dynamic>;
- return body.map((dynamic it) => IssueLabel.fromJson(it as Map<String, dynamic>)).toList();
- }
-
- /// Returns changed files for a [PullRequest].
- ///
- /// See more:
- /// * https://developer.github.com/v3/pulls/#list-pull-requests-files
- Future<List<String>> listFiles(PullRequest pullRequest) async {
- final List<PullRequestFile> files =
- await github.pullRequests.listFiles(pullRequest.base!.repo!.slug(), pullRequest.number!).toList();
- log.fine('List of files: $files');
- return files.map((PullRequestFile file) {
- return file.filename!;
- }).toList();
- }
-
- /// Gets the file content as UTF8 string of the file specified by the `path`
- /// in the repository.
- Future<String> getFileContent(RepositorySlug slug, String path, {String? ref}) async {
- ArgumentError.checkNotNull(slug);
- ArgumentError.checkNotNull(path);
- final RepositoryContents contents = await github.repositories.getContents(slug, path, ref: ref);
- if (!contents.isFile) {
- throw 'The path $path should point to a file, but it is not!';
- }
- final String content = utf8.decode(base64.decode(contents.file!.content!.replaceAll('\n', '')));
- return content;
- }
-
- /// Gets the reference of a specific branch in the repository.
- Future<GitReference> getReference(RepositorySlug slug, String ref) {
- ArgumentError.checkNotNull(slug);
- ArgumentError.checkNotNull(ref);
- return github.git.getReference(slug, ref);
- }
-
- /// Returns JSON of the current GitHub API quota usage.
- ///
- /// This does not consume any API usage.
- ///
- /// Reference:
- /// * https://docs.github.com/en/rest/reference/rate-limit
- Future<RateLimit> getRateLimit() => github.misc.getRateLimit();
-
- CurrentUser? _currentUser;
-
- Future<CurrentUser?> _getCurrentUser() async {
- _currentUser ??= await github.users.getCurrentUser();
- return _currentUser;
- }
-
- Future<RepositorySlug> _getCurrentUserSlug(String repository) async {
- return RepositorySlug((await _getCurrentUser())!.login!, repository);
- }
-
- String _generateNewRef() {
- const String chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890';
- final Random rnd = Random();
- return String.fromCharCodes(Iterable<int>.generate(10, (_) => chars.codeUnitAt(rnd.nextInt(chars.length))));
- }
-
- /// Returns a [List] of [Issue]s that match the given [query].
- ///
- /// The GitHub package uses the [Issue] object for both issue results and PRs.
- ///
- /// Reference:
- /// * https://docs.github.com/en/rest/search?apiVersion=2022-11-28#search-issues-and-pull-requests
- /// * https://docs.github.com/en/search-github/searching-on-github/searching-issues-and-pull-requests
- Future<List<Issue>> searchIssuesAndPRs(
- RepositorySlug slug,
- String query, {
- String? sort,
- int pages = 2,
- }) {
- return github.search
- .issues(
- Uri.encodeComponent('$query repo:${slug.fullName}'),
- sort: sort,
- pages: pages,
- )
- .toList();
- }
-
- /// Retrieves a pull request with the given [number].
- ///
- /// Reference:
- /// * https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#get-a-pull-request
- Future<PullRequest> getPullRequest(RepositorySlug slug, int number) async {
- return github.pullRequests.get(slug, number);
- }
-}
diff --git a/app_dart/lib/src/service/logging.dart b/app_dart/lib/src/service/logging.dart
deleted file mode 100644
index 1498eea..0000000
--- a/app_dart/lib/src/service/logging.dart
+++ /dev/null
@@ -1,7 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:logging/logging.dart';
-
-final Logger log = Logger.root..level = Level.ALL;
diff --git a/app_dart/lib/src/service/luci_build_service.dart b/app_dart/lib/src/service/luci_build_service.dart
deleted file mode 100644
index fb23f4a..0000000
--- a/app_dart/lib/src/service/luci_build_service.dart
+++ /dev/null
@@ -1,728 +0,0 @@
-// Copyright 2020 The Flutter 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:async';
-import 'dart:convert';
-import 'dart:math';
-import 'dart:typed_data';
-
-import 'package:github/github.dart' as github;
-import 'package:github/hooks.dart';
-import 'package:googleapis/pubsub/v1.dart';
-
-import '../foundation/github_checks_util.dart';
-import '../foundation/utils.dart';
-import '../model/appengine/commit.dart';
-import '../model/appengine/task.dart';
-import '../model/ci_yaml/target.dart';
-import '../model/github/checks.dart' as cocoon_checks;
-import '../model/luci/buildbucket.dart';
-import '../model/luci/push_message.dart' as push_message;
-import '../request_handling/pubsub.dart';
-import '../service/datastore.dart';
-import '../service/logging.dart';
-import 'buildbucket.dart';
-import 'cache_service.dart';
-import 'config.dart';
-import 'exceptions.dart';
-import 'gerrit_service.dart';
-
-const Set<String> taskFailStatusSet = <String>{
- Task.statusInfraFailure,
- Task.statusFailed,
- Task.statusCancelled,
-};
-
-/// Class to interact with LUCI buildbucket to get, trigger
-/// and cancel builds for github repos. It uses [config.luciTryBuilders] to
-/// get the list of available builders.
-class LuciBuildService {
- LuciBuildService({
- required this.config,
- required this.cache,
- required this.buildBucketClient,
- GithubChecksUtil? githubChecksUtil,
- GerritService? gerritService,
- this.pubsub = const PubSub(),
- }) : githubChecksUtil = githubChecksUtil ?? const GithubChecksUtil(),
- gerritService = gerritService ?? GerritService(config: config);
-
- BuildBucketClient buildBucketClient;
- final CacheService cache;
- Config config;
- GithubChecksUtil githubChecksUtil;
- GerritService gerritService;
-
- final PubSub pubsub;
-
- static const Set<Status> failStatusSet = <Status>{Status.canceled, Status.failure, Status.infraFailure};
-
- static const int kBackfillPriority = 35;
- static const int kDefaultPriority = 30;
- static const int kRerunPriority = 29;
-
- /// Github labels have a max length of 100, so conserve chars here.
- /// This is currently used by packages repo only.
- /// See: https://github.com/flutter/flutter/issues/130076
- static const String githubBuildLabelPrefix = 'override:';
- static const String propertiesGithubBuildLabelName = 'overrides';
-
- /// Name of the subcache to store luci build related values in redis.
- static const String subCacheName = 'luci';
-
- /// Shards [rows] into several sublists of size [maxEntityGroups].
- Future<List<List<Request>>> shard(List<Request> requests, int max) async {
- final List<List<Request>> shards = <List<Request>>[];
- for (int i = 0; i < requests.length; i += max) {
- shards.add(requests.sublist(i, i + min<int>(requests.length - i, max)));
- }
- return shards;
- }
-
- /// Returns an Iterable of try BuildBucket build for a given Github [slug], [sha], [builderName].
- Future<Iterable<Build>> getTryBuilds(
- github.RepositorySlug slug,
- String sha,
- String? builderName,
- ) async {
- final Map<String, List<String>> tags = <String, List<String>>{
- 'buildset': <String>['sha/git/$sha'],
- 'user_agent': const <String>['flutter-cocoon'],
- };
- return getBuilds(slug, sha, builderName, 'try', tags);
- }
-
- /// Returns an Iterable of try Buildbucket [Build]s for a given [PullRequest].
- Future<Iterable<Build>> getTryBuildsByPullRequest(
- github.PullRequest pullRequest,
- ) async {
- final github.RepositorySlug slug = pullRequest.base!.repo!.slug();
- final Map<String, List<String>> tags = <String, List<String>>{
- 'buildset': <String>['pr/git/${pullRequest.number}'],
- 'github_link': <String>['https://github.com/${slug.fullName}/pull/${pullRequest.number}'],
- 'user_agent': const <String>['flutter-cocoon'],
- };
- return getBuilds(slug, null, null, 'try', tags);
- }
-
- /// Returns an Iterable of prod BuildBucket build for a given Github [slug], [commitSha],
- /// [builderName] and [repo].
- Future<Iterable<Build>> getProdBuilds(
- github.RepositorySlug slug,
- String commitSha,
- String? builderName,
- ) async {
- final Map<String, List<String>> tags = <String, List<String>>{};
- return getBuilds(slug, commitSha, builderName, 'prod', tags);
- }
-
- /// Returns an iterable of BuildBucket builds for a given Github [slug], [commitSha],
- /// [builderName], [bucket] and [tags].
- Future<Iterable<Build>> getBuilds(
- github.RepositorySlug? slug,
- String? commitSha,
- String? builderName,
- String bucket,
- Map<String, List<String>> tags,
- ) async {
- final BatchResponse batch = await buildBucketClient.batch(
- BatchRequest(
- requests: <Request>[
- Request(
- searchBuilds: SearchBuildsRequest(
- predicate: BuildPredicate(
- builderId: BuilderId(
- project: 'flutter',
- bucket: bucket,
- builder: builderName,
- ),
- tags: tags,
- ),
- fields: 'builds.*.id,builds.*.builder,builds.*.tags,builds.*.status,builds.*.input.properties',
- ),
- ),
- ],
- ),
- );
-
- log.info('Reponses from get builds batch request = ${batch.responses!.length}');
- for (Response response in batch.responses!) {
- log.info('Found a response: ${response.toString()}');
- }
-
- final Iterable<Build> builds = batch.responses!
- .map((Response response) => response.searchBuilds)
- .expand((SearchBuildsResponse? response) => response!.builds ?? <Build>[]);
- return builds;
- }
-
- /// Schedules presubmit [targets] on BuildBucket for [pullRequest].
- Future<List<Target>> scheduleTryBuilds({
- required List<Target> targets,
- required github.PullRequest pullRequest,
- CheckSuiteEvent? checkSuiteEvent,
- }) async {
- if (targets.isEmpty) {
- return targets;
- }
-
- final List<Request> requests = <Request>[];
- final List<String> branches = await gerritService.branches(
- 'flutter-review.googlesource.com',
- 'recipes',
- filterRegex: 'flutter-.*|fuchsia.*',
- );
- log.info('Available release branches: $branches');
-
- final String sha = pullRequest.head!.sha!;
- String cipdVersion = 'refs/heads/${pullRequest.base!.ref!}';
- cipdVersion = branches.contains(cipdVersion) ? cipdVersion : config.defaultRecipeBundleRef;
-
- for (Target target in targets) {
- final github.CheckRun checkRun = await githubChecksUtil.createCheckRun(
- config,
- target.slug,
- sha,
- target.value.name,
- );
-
- final github.RepositorySlug slug = pullRequest.base!.repo!.slug();
-
- final Map<String, dynamic> userData = <String, dynamic>{
- 'builder_name': target.value.name,
- 'check_run_id': checkRun.id,
- 'commit_sha': sha,
- 'commit_branch': pullRequest.base!.ref!.replaceAll('refs/heads/', ''),
- };
-
- final Map<String, List<String>> tags = <String, List<String>>{
- 'github_checkrun': <String>[checkRun.id.toString()],
- };
-
- final Map<String, Object> properties = target.getProperties();
- properties.putIfAbsent('git_branch', () => pullRequest.base!.ref!.replaceAll('refs/heads/', ''));
-
- final List<String>? labels = pullRequest.labels
- ?.where((label) => label.name.startsWith(githubBuildLabelPrefix))
- .map((obj) => obj.name)
- .toList();
-
- if (labels != null && labels.isNotEmpty) {
- properties[propertiesGithubBuildLabelName] = labels;
- }
-
- requests.add(
- Request(
- scheduleBuild: _createPresubmitScheduleBuild(
- slug: slug,
- sha: pullRequest.head!.sha!,
- //Use target.value.name here otherwise tests will die due to null checkRun.name.
- checkName: target.value.name,
- pullRequestNumber: pullRequest.number!,
- cipdVersion: cipdVersion,
- userData: userData,
- properties: properties,
- tags: tags,
- dimensions: target.getDimensions(),
- ),
- ),
- );
- }
-
- final Iterable<List<Request>> requestPartitions = await shard(requests, config.schedulingShardSize);
- for (List<Request> requestPartition in requestPartitions) {
- final BatchRequest batchRequest = BatchRequest(requests: requestPartition);
- await pubsub.publish('scheduler-requests', batchRequest);
- }
-
- return targets;
- }
-
- /// Cancels all the current builds on [pullRequest] with [reason].
- ///
- /// Builds are queried based on the [RepositorySlug] and pull request number.
- Future<void> cancelBuilds(github.PullRequest pullRequest, String reason) async {
- log.info(
- 'Attempting to cancel builds for pullrequest ${pullRequest.base!.repo!.fullName}/${pullRequest.number}',
- );
-
- final Iterable<Build> builds = await getTryBuildsByPullRequest(pullRequest);
- log.info('Found ${builds.length} builds.');
-
- if (builds.isEmpty) {
- log.warning('No builds were found for pull request ${pullRequest.base!.repo!.fullName}.');
- return;
- }
-
- final List<Request> requests = <Request>[];
- for (Build build in builds) {
- if (build.status == Status.scheduled || build.status == Status.started) {
- // Scheduled status includes scheduled and pending tasks.
- log.info('Cancelling build with build id ${build.id}.');
- requests.add(
- Request(
- cancelBuild: CancelBuildRequest(
- id: build.id,
- summaryMarkdown: reason,
- ),
- ),
- );
- }
- }
-
- if (requests.isNotEmpty) {
- await buildBucketClient.batch(BatchRequest(requests: requests));
- }
- }
-
- /// Filters [builders] to only those that failed on [pullRequest].
- Future<List<Build?>> failedBuilds(
- github.PullRequest pullRequest,
- List<Target> targets,
- ) async {
- final Iterable<Build> builds = await getTryBuilds(pullRequest.base!.repo!.slug(), pullRequest.head!.sha!, null);
- final Iterable<String> builderNames = targets.map((Target target) => target.value.name);
- // Return only builds that exist in the configuration file.
- final Iterable<Build?> failedBuilds = builds.where((Build? build) => failStatusSet.contains(build!.status));
- final Iterable<Build?> expectedFailedBuilds =
- failedBuilds.where((Build? build) => builderNames.contains(build!.builderId.builder));
- return expectedFailedBuilds.toList();
- }
-
- /// Sends [ScheduleBuildRequest] using information from a given build's
- /// [BuildPushMessage].
- ///
- /// The buildset, user_agent, and github_link tags are applied to match the
- /// original build. The build properties and user data from the original build
- /// are also preserved.
- ///
- /// The [currentAttempt] is used to track the number of current build attempt.
- Future<Build> rescheduleBuild({
- required String builderName,
- required push_message.BuildPushMessage buildPushMessage,
- required int rescheduleAttempt,
- }) async {
- // Ensure we are using V2 bucket name istead of V1.
- // V1 bucket name is "luci.flutter.prod" while the api
- // is expecting just the last part after "."(prod).
- final String bucketName = buildPushMessage.build!.bucket!.split('.').last;
- final Map<String, List<String>> tags = <String, List<String>>{
- 'buildset': buildPushMessage.build!.tagsByName('buildset'),
- 'user_agent': buildPushMessage.build!.tagsByName('user_agent'),
- 'github_link': buildPushMessage.build!.tagsByName('github_link'),
- 'cipd_version': buildPushMessage.build!.tagsByName('cipd_version'),
- 'github_checkrun': buildPushMessage.build!.tagsByName('github_checkrun'),
- 'current_attempt': <String>[rescheduleAttempt.toString()],
- };
- return buildBucketClient.scheduleBuild(
- ScheduleBuildRequest(
- builderId: BuilderId(
- project: buildPushMessage.build!.project,
- bucket: bucketName,
- builder: builderName,
- ),
- tags: tags,
- // We need to cast to <String, Object> to bypass json.encode error when scheduling builds.
- properties:
- (buildPushMessage.build!.buildParameters!['properties'] as Map<String, Object?>).cast<String, Object>(),
- notify: NotificationConfig(
- pubsubTopic: 'projects/flutter-dashboard/topics/luci-builds',
- userData: base64Encode(json.encode(buildPushMessage.userData).codeUnits),
- ),
- ),
- );
- }
-
- /// Sends presubmit [ScheduleBuildRequest] for a pull request using [checkRunEvent].
- ///
- /// Returns the [Build] returned by scheduleBuildRequest.
- Future<Build> reschedulePresubmitBuildUsingCheckRunEvent(cocoon_checks.CheckRunEvent checkRunEvent) async {
- final github.RepositorySlug slug = checkRunEvent.repository!.slug();
-
- final String sha = checkRunEvent.checkRun!.headSha!;
- final String checkName = checkRunEvent.checkRun!.name!;
-
- final github.CheckRun githubCheckRun = await githubChecksUtil.createCheckRun(config, slug, sha, checkName);
-
- final Iterable<Build> builds = await getTryBuilds(slug, sha, checkName);
- if (builds.isEmpty) {
- throw NoBuildFoundException('Unable to find try build.');
- }
-
- final Build build = builds.first;
- final String prString = build.tags!['buildset']!.firstWhere((String? element) => element!.startsWith('pr/git/'))!;
- final String cipdVersion = build.tags!['cipd_version']![0]!;
- final String githubLink = build.tags!['github_link']![0]!;
- final String repoName = githubLink.split('/')[4];
- final String branch = Config.defaultBranch(github.RepositorySlug('flutter', repoName));
- final int prNumber = int.parse(prString.split('/')[2]);
-
- final Map<String, dynamic> userData = <String, dynamic>{
- 'check_run_id': githubCheckRun.id,
- 'commit_branch': branch,
- 'commit_sha': sha,
- };
- final Map<String, Object>? properties = build.input!.properties;
- log.info('input ${build.input!} properties $properties');
-
- final ScheduleBuildRequest scheduleBuildRequest = _createPresubmitScheduleBuild(
- slug: slug,
- sha: sha,
- checkName: checkName,
- pullRequestNumber: prNumber,
- cipdVersion: cipdVersion,
- properties: properties,
- userData: userData,
- );
-
- final Build scheduleBuild = await buildBucketClient.scheduleBuild(scheduleBuildRequest);
- final String buildUrl = 'https://ci.chromium.org/ui/b/${scheduleBuild.id}';
- await githubChecksUtil.updateCheckRun(config, slug, githubCheckRun, detailsUrl: buildUrl);
- return scheduleBuild;
- }
-
- /// Sends postsubmit [ScheduleBuildRequest] for a commit using [checkRunEvent], [Commit], [Task], and [Target].
- ///
- /// Returns the [Build] returned by scheduleBuildRequest.
- Future<Build> reschedulePostsubmitBuildUsingCheckRunEvent(
- cocoon_checks.CheckRunEvent checkRunEvent, {
- required Commit commit,
- required Task task,
- required Target target,
- }) async {
- final github.RepositorySlug slug = checkRunEvent.repository!.slug();
- final String sha = checkRunEvent.checkRun!.headSha!;
- final String checkName = checkRunEvent.checkRun!.name!;
-
- final Iterable<Build> builds = await getProdBuilds(slug, sha, checkName);
- if (builds.isEmpty) {
- throw NoBuildFoundException('Unable to find prod build.');
- }
-
- final Build build = builds.first;
- final Map<String, Object>? properties = build.input!.properties;
- log.info('input ${build.input!} properties $properties');
-
- final ScheduleBuildRequest scheduleBuildRequest =
- await _createPostsubmitScheduleBuild(commit: commit, target: target, task: task, properties: properties);
- final Build scheduleBuild = await buildBucketClient.scheduleBuild(scheduleBuildRequest);
- return scheduleBuild;
- }
-
- /// Gets [Build] using its [id] and passing the additional
- /// fields to be populated in the response.
- Future<Build> getBuildById(String? id, {String? fields}) async {
- final GetBuildRequest request = GetBuildRequest(id: id, fields: fields);
- return buildBucketClient.getBuild(request);
- }
-
- /// Gets builder list whose config is pre-defined in LUCI.
- ///
- /// Returns cache if existing. Otherwise make the RPC call to fetch list.
- Future<Set<String>> getAvailableBuilderSet({
- String project = 'flutter',
- String bucket = 'prod',
- }) async {
- final Uint8List? cacheValue = await cache.getOrCreate(
- subCacheName,
- 'builderlist',
- createFn: () => _getAvailableBuilderSet(project: project, bucket: bucket),
- // New commit triggering tasks should be finished within 5 mins.
- // The batch backfiller's execution frequency is also 5 mins.
- ttl: const Duration(minutes: 5),
- );
-
- return Set.from(String.fromCharCodes(cacheValue!).split(','));
- }
-
- /// Returns cache if existing, otherwise makes the RPC call to fetch list.
- ///
- /// Use [token] to make sure obtain all the list by calling RPC multiple times.
- Future<Uint8List> _getAvailableBuilderSet({
- String project = 'flutter',
- String bucket = 'prod',
- }) async {
- log.info('No cached value for builderList, start fetching via the rpc call.');
- final Set<String> availableBuilderSet = <String>{};
- String? token;
- do {
- final ListBuildersResponse listBuildersResponse = await buildBucketClient.listBuilders(
- ListBuildersRequest(
- project: project,
- bucket: bucket,
- pageToken: token,
- ),
- );
- final List<String> availableBuilderList = listBuildersResponse.builders!.map((e) => e.id!.builder!).toList();
- availableBuilderSet.addAll(<String>{...availableBuilderList});
- token = listBuildersResponse.nextPageToken;
- } while (token != null);
- final String joinedBuilderSet = availableBuilderSet.toList().join(',');
- log.info('successfully fetched the builderSet: $joinedBuilderSet');
- return Uint8List.fromList(joinedBuilderSet.codeUnits);
- }
-
- /// Schedules list of post-submit builds deferring work to [schedulePostsubmitBuild].
- ///
- /// Returns empty list if all targets are successfully published to pub/sub. Otherwise,
- /// returns the original list.
- Future<List<Tuple<Target, Task, int>>> schedulePostsubmitBuilds({
- required Commit commit,
- required List<Tuple<Target, Task, int>> toBeScheduled,
- }) async {
- if (toBeScheduled.isEmpty) {
- log.fine('Skipping schedulePostsubmitBuilds as there are no targets to be scheduled by Cocoon');
- return toBeScheduled;
- }
- final List<Request> buildRequests = <Request>[];
- Set<String> availableBuilderSet;
- try {
- availableBuilderSet = await getAvailableBuilderSet(project: 'flutter', bucket: 'prod');
- } catch (error) {
- log.severe('Failed to get buildbucket builder list due to $error');
- return toBeScheduled;
- }
- log.info('Available builder list: $availableBuilderSet');
- for (Tuple<Target, Task, int> tuple in toBeScheduled) {
- // Non-existing builder target will be skipped from scheduling.
- if (!availableBuilderSet.contains(tuple.first.value.name)) {
- log.warning('Found no available builder for ${tuple.first.value.name}, commit ${commit.sha}');
- continue;
- }
- log.info('create postsubmit schedule request for target: ${tuple.first.value} in commit ${commit.sha}');
- final ScheduleBuildRequest scheduleBuildRequest = await _createPostsubmitScheduleBuild(
- commit: commit,
- target: tuple.first,
- task: tuple.second,
- priority: tuple.third,
- );
- buildRequests.add(Request(scheduleBuild: scheduleBuildRequest));
- log.info('created postsubmit schedule request for target: ${tuple.first.value} in commit ${commit.sha}');
- }
- final BatchRequest batchRequest = BatchRequest(requests: buildRequests);
- log.fine(batchRequest);
- List<String> messageIds;
- try {
- messageIds = await pubsub.publish('scheduler-requests', batchRequest);
- log.info('Published $messageIds for commit ${commit.sha}');
- } catch (error) {
- log.severe('Failed to publish message to pub/sub due to $error');
- return toBeScheduled;
- }
- log.info('Published a request with ${buildRequests.length} builds');
- return <Tuple<Target, Task, int>>[];
- }
-
- /// Create a Presubmit ScheduleBuildRequest using the [slug], [sha], and
- /// [checkName] for the provided [build] with the provided [checkRunId].
- ScheduleBuildRequest _createPresubmitScheduleBuild({
- required github.RepositorySlug slug,
- required String sha,
- required String checkName,
- required int pullRequestNumber,
- required String cipdVersion,
- Map<String, Object>? properties,
- Map<String, List<String>>? tags,
- Map<String, dynamic>? userData,
- List<RequestedDimension>? dimensions,
- }) {
- final Map<String, Object> processedProperties = <String, Object>{};
- processedProperties.addAll(properties ?? <String, Object>{});
- processedProperties.addEntries(
- <String, Object>{
- 'git_url': 'https://github.com/${slug.owner}/${slug.name}',
- 'git_ref': 'refs/pull/$pullRequestNumber/head',
- 'exe_cipd_version': cipdVersion,
- }.entries,
- );
-
- final Map<String, dynamic> processedUserData = userData ?? <String, dynamic>{};
- processedUserData['repo_owner'] = slug.owner;
- processedUserData['repo_name'] = slug.name;
- processedUserData['user_agent'] = 'flutter-cocoon';
-
- final BuilderId builderId = BuilderId(project: 'flutter', bucket: 'try', builder: checkName);
-
- final Map<String, List<String>> processedTags = tags ?? <String, List<String>>{};
- processedTags['buildset'] = <String>['pr/git/$pullRequestNumber', 'sha/git/$sha'];
- processedTags['user_agent'] = const <String>['flutter-cocoon'];
- processedTags['github_link'] = <String>['https://github.com/${slug.owner}/${slug.name}/pull/$pullRequestNumber'];
- processedTags['cipd_version'] = <String>[cipdVersion];
-
- final NotificationConfig notificationConfig = NotificationConfig(
- pubsubTopic: 'projects/flutter-dashboard/topics/luci-builds',
- userData: base64Encode(json.encode(processedUserData).codeUnits),
- );
-
- final Map<String, dynamic> exec = <String, dynamic>{'cipdVersion': cipdVersion};
-
- return ScheduleBuildRequest(
- builderId: builderId,
- tags: processedTags,
- properties: processedProperties,
- notify: notificationConfig,
- fields: 'id,builder,number,status,tags',
- exe: exec,
- dimensions: dimensions,
- );
- }
-
- /// Creates a [ScheduleBuildRequest] for [target] and [task] against [commit].
- ///
- /// By default, build [priority] is increased for release branches.
- Future<ScheduleBuildRequest> _createPostsubmitScheduleBuild({
- required Commit commit,
- required Target target,
- required Task task,
- Map<String, Object>? properties,
- Map<String, List<String>>? tags,
- int priority = kDefaultPriority,
- }) async {
- tags ??= <String, List<String>>{};
- tags.addAll(<String, List<String>>{
- 'buildset': <String>[
- 'commit/git/${commit.sha}',
- 'commit/gitiles/flutter.googlesource.com/mirrors/${commit.slug.name}/+/${commit.sha}',
- ],
- });
-
- final String commitKey = task.parentKey!.id.toString();
- final String taskKey = task.key.id.toString();
- log.info('Scheduling builder: ${target.value.name} for commit ${commit.sha}');
- log.info('Task commit_key: $commitKey for task name: ${task.name}');
- log.info('Task task_key: $taskKey for task name: ${task.name}');
-
- final Map<String, dynamic> rawUserData = <String, dynamic>{
- 'commit_key': commitKey,
- 'task_key': taskKey,
- };
-
- // Creates post submit checkrun only for unflaky targets from [config.postsubmitSupportedRepos].
- if (!target.value.bringup && config.postsubmitSupportedRepos.contains(target.slug)) {
- await createPostsubmitCheckRun(commit, target, rawUserData);
- }
-
- tags['user_agent'] = <String>['flutter-cocoon'];
- // Tag `scheduler_job_id` is needed when calling buildbucket search build API.
- tags['scheduler_job_id'] = <String>['flutter/${target.value.name}'];
- final Map<String, Object> processedProperties = target.getProperties();
- processedProperties.addAll(properties ?? <String, Object>{});
- processedProperties['git_branch'] = commit.branch!;
- final String cipdVersion = 'refs/heads/${commit.branch}';
- processedProperties['exe_cipd_version'] = cipdVersion;
- return ScheduleBuildRequest(
- builderId: BuilderId(
- project: 'flutter',
- bucket: target.getBucket(),
- builder: target.value.name,
- ),
- dimensions: target.getDimensions(),
- exe: <String, dynamic>{
- 'cipdVersion': cipdVersion,
- },
- gitilesCommit: GitilesCommit(
- project: 'mirrors/${commit.slug.name}',
- host: 'flutter.googlesource.com',
- ref: 'refs/heads/${commit.branch}',
- hash: commit.sha,
- ),
- notify: NotificationConfig(
- pubsubTopic: 'projects/flutter-dashboard/topics/luci-builds-prod',
- userData: base64Encode(json.encode(rawUserData).codeUnits),
- ),
- tags: tags,
- properties: processedProperties,
- priority: priority,
- );
- }
-
- /// Creates postsubmit check runs for prod targets in supported repositories.
- Future<void> createPostsubmitCheckRun(
- Commit commit,
- Target target,
- Map<String, dynamic> rawUserData,
- ) async {
- final github.CheckRun checkRun = await githubChecksUtil.createCheckRun(
- config,
- target.slug,
- commit.sha!,
- target.value.name,
- );
- rawUserData['check_run_id'] = checkRun.id;
- rawUserData['commit_sha'] = commit.sha;
- rawUserData['commit_branch'] = commit.branch;
- rawUserData['builder_name'] = target.value.name;
- rawUserData['repo_owner'] = target.slug.owner;
- rawUserData['repo_name'] = target.slug.name;
- }
-
- /// Check to auto-rerun TOT test failures.
- ///
- /// A builder will be retried if:
- /// 1. It has been tried below the max retry limit
- /// 2. It is for the tip of tree
- /// 3. The last known status is not green
- /// 4. [ignoreChecks] is false. This allows manual reruns to bypass the Cocoon state.
- Future<bool> checkRerunBuilder({
- required Commit commit,
- required Target target,
- required Task task,
- required DatastoreService datastore,
- Map<String, List<String>>? tags,
- bool ignoreChecks = false,
- }) async {
- if (ignoreChecks == false && await _shouldRerunBuilder(task, commit, datastore) == false) {
- return false;
- }
- log.info('Rerun builder: ${target.value.name} for commit ${commit.sha}');
- tags ??= <String, List<String>>{};
- tags['trigger_type'] = <String>['retry'];
-
- final BatchRequest request = BatchRequest(
- requests: <Request>[
- Request(
- scheduleBuild: await _createPostsubmitScheduleBuild(
- commit: commit,
- target: target,
- task: task,
- priority: kRerunPriority,
- properties: Config.defaultProperties,
- tags: tags,
- ),
- ),
- ],
- );
- await pubsub.publish('scheduler-requests', request);
-
- task.attempts = (task.attempts ?? 0) + 1;
- // Mark task as in progress to ensure it isn't scheduled over
- task.status = Task.statusInProgress;
- await datastore.insert(<Task>[task]);
-
- return true;
- }
-
- /// Check if a builder should be rerun.
- ///
- /// A rerun happens when a build fails, the retry number hasn't reached the limit, and the build is on TOT.
- Future<bool> _shouldRerunBuilder(Task task, Commit commit, DatastoreService? datastore) async {
- if (!taskFailStatusSet.contains(task.status)) {
- return false;
- }
- final int retries = task.attempts ?? 1;
- if (retries > config.maxLuciTaskRetries) {
- log.warning('Max retries reached');
- return false;
- }
-
- final Commit latestCommit = await datastore!
- .queryRecentCommits(
- limit: 1,
- slug: commit.slug,
- branch: commit.branch,
- )
- .single;
- return latestCommit.sha == commit.sha;
- }
-}
diff --git a/app_dart/lib/src/service/scheduler.dart b/app_dart/lib/src/service/scheduler.dart
deleted file mode 100644
index fb33d6c..0000000
--- a/app_dart/lib/src/service/scheduler.dart
+++ /dev/null
@@ -1,608 +0,0 @@
-// Copyright 2021 The Flutter 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:math';
-import 'dart:typed_data';
-
-import 'package:cocoon_service/src/service/exceptions.dart';
-import 'package:cocoon_service/src/service/build_status_provider.dart';
-import 'package:cocoon_service/src/service/scheduler/policy.dart';
-import 'package:gcloud/db.dart';
-import 'package:github/github.dart' as github;
-import 'package:github/github.dart';
-import 'package:github/hooks.dart';
-import 'package:googleapis/bigquery/v2.dart';
-import 'package:retry/retry.dart';
-import 'package:truncate/truncate.dart';
-import 'package:yaml/yaml.dart';
-
-import '../foundation/providers.dart';
-import '../foundation/typedefs.dart';
-import '../foundation/utils.dart';
-import '../model/appengine/commit.dart';
-import '../model/appengine/task.dart';
-import '../model/ci_yaml/ci_yaml.dart';
-import '../model/ci_yaml/target.dart';
-import '../model/github/checks.dart' as cocoon_checks;
-import '../model/luci/buildbucket.dart';
-import '../model/proto/internal/scheduler.pb.dart' as pb;
-import '../service/logging.dart';
-import 'cache_service.dart';
-import 'config.dart';
-import 'datastore.dart';
-import 'github_checks_service.dart';
-import 'github_service.dart';
-import 'luci_build_service.dart';
-
-/// Scheduler service to validate all commits to supported Flutter repositories.
-///
-/// Scheduler responsibilties include:
-/// 1. Tracking commits in Cocoon
-/// 2. Ensuring commits are validated (via scheduling tasks against commits)
-/// 3. Retry mechanisms for tasks
-class Scheduler {
- Scheduler({
- required this.cache,
- required this.config,
- required this.githubChecksService,
- required this.luciBuildService,
- this.datastoreProvider = DatastoreService.defaultProvider,
- this.httpClientProvider = Providers.freshHttpClient,
- this.buildStatusProvider = BuildStatusService.defaultProvider,
- });
-
- final BuildStatusServiceProvider buildStatusProvider;
- final CacheService cache;
- final Config config;
- final DatastoreServiceProvider datastoreProvider;
- final GithubChecksService githubChecksService;
- final HttpClientProvider httpClientProvider;
-
- late DatastoreService datastore;
- LuciBuildService luciBuildService;
-
- /// Name of the subcache to store scheduler related values in redis.
- static const String subcacheName = 'scheduler';
-
- static const String kCiYamlCheckName = 'ci.yaml validation';
-
- /// Ensure [commits] exist in Cocoon.
- ///
- /// If [Commit] does not exist in Datastore:
- /// * Write it to datastore
- /// * Schedule tasks listed in its scheduler config
- /// Otherwise, ignore it.
- Future<void> addCommits(List<Commit> commits) async {
- datastore = datastoreProvider(config.db);
- final List<Commit> newCommits = await _getMissingCommits(commits);
- log.fine('Found ${newCommits.length} new commits on GitHub');
- for (Commit commit in newCommits) {
- await _addCommit(commit);
- }
- }
-
- /// Schedule tasks against [PullRequest].
- ///
- /// If [PullRequest] was merged, schedule prod tasks against it.
- /// Otherwise if it is presubmit, schedule try tasks against it.
- Future<void> addPullRequest(github.PullRequest pr) async {
- datastore = datastoreProvider(config.db);
- // TODO(chillers): Support triggering on presubmit. https://github.com/flutter/flutter/issues/77858
- if (!pr.merged!) {
- log.warning('Only pull requests that were closed and merged should have tasks scheduled');
- return;
- }
-
- final String fullRepo = pr.base!.repo!.fullName;
- final String? branch = pr.base!.ref;
- final String sha = pr.mergeCommitSha!;
-
- final String id = '$fullRepo/$branch/$sha';
- final Key<String> key = datastore.db.emptyKey.append<String>(Commit, id: id);
- final Commit mergedCommit = Commit(
- author: pr.user!.login!,
- authorAvatarUrl: pr.user!.avatarUrl!,
- branch: branch,
- key: key,
- // The field has a max length of 1500 so ensure the commit message is not longer.
- message: truncate(pr.title!, 1490, omission: '...'),
- repository: fullRepo,
- sha: sha,
- timestamp: pr.mergedAt!.millisecondsSinceEpoch,
- );
-
- if (await _commitExistsInDatastore(mergedCommit)) {
- log.fine('$sha already exists in datastore. Scheduling skipped.');
- return;
- }
-
- log.fine('Scheduling $sha via GitHub webhook');
- await _addCommit(mergedCommit);
- }
-
- /// Processes postsubmit tasks.
- Future<void> _addCommit(Commit commit) async {
- if (!config.supportedRepos.contains(commit.slug)) {
- log.fine('Skipping ${commit.id} as repo is not supported');
- return;
- }
-
- final CiYaml ciYaml = await getCiYaml(commit);
-
- final List<Target> initialTargets = ciYaml.getInitialTargets(ciYaml.postsubmitTargets);
- final List<Task> tasks = targetsToTask(commit, initialTargets).toList();
-
- final List<Tuple<Target, Task, int>> toBeScheduled = <Tuple<Target, Task, int>>[];
- for (Target target in initialTargets) {
- final Task task = tasks.singleWhere((Task task) => task.name == target.value.name);
- SchedulerPolicy policy = target.schedulerPolicy;
- // Release branches should run every task
- if (Config.defaultBranch(commit.slug) != commit.branch) {
- policy = GuaranteedPolicy();
- }
- final int? priority = await policy.triggerPriority(task: task, datastore: datastore);
- if (priority != null) {
- // Mark task as in progress to ensure it isn't scheduled over
- task.status = Task.statusInProgress;
- toBeScheduled.add(Tuple<Target, Task, int>(target, task, priority));
- }
- }
-
- // Datastore must be written to generate task keys
- try {
- await datastore.withTransaction<void>((Transaction transaction) async {
- transaction.queueMutations(inserts: <Commit>[commit]);
- transaction.queueMutations(inserts: tasks);
- await transaction.commit();
- log.fine('Committed ${tasks.length} new tasks for commit ${commit.sha!}');
- });
- } catch (error) {
- log.severe('Failed to add commit ${commit.sha!}: $error');
- }
-
- await _batchScheduleBuilds(commit, toBeScheduled);
- await _uploadToBigQuery(commit);
- }
-
- /// Schedule all builds in batch requests instead of a single request.
- ///
- /// Each batch request contains [Config.batchSize] builds to be scheduled.
- Future<void> _batchScheduleBuilds(Commit commit, List<Tuple<Target, Task, int>> toBeScheduled) async {
- log.info('Batching ${toBeScheduled.length} for ${commit.sha}');
- final List<Future<void>> futures = <Future<void>>[];
- for (int i = 0; i < toBeScheduled.length; i += config.batchSize) {
- futures.add(
- luciBuildService.schedulePostsubmitBuilds(
- commit: commit,
- toBeScheduled: toBeScheduled.sublist(i, min(i + config.batchSize, toBeScheduled.length)),
- ),
- );
- }
- await Future.wait<void>(futures);
- }
-
- /// Return subset of [commits] not stored in Datastore.
- Future<List<Commit>> _getMissingCommits(List<Commit> commits) async {
- final List<Commit> newCommits = <Commit>[];
- // Ensure commits are sorted from newest to oldest (descending order)
- commits.sort((Commit a, Commit b) => b.timestamp!.compareTo(a.timestamp!));
- for (Commit commit in commits) {
- // Cocoon may randomly drop commits, so check the entire list.
- if (!await _commitExistsInDatastore(commit)) {
- newCommits.add(commit);
- }
- }
-
- // Reverses commits to be in order of oldest to newest.
- return newCommits;
- }
-
- /// Whether [Commit] already exists in [datastore].
- ///
- /// Datastore is Cocoon's source of truth for what commits have been scheduled.
- /// Since webhooks or cron jobs can schedule commits, we must verify a commit
- /// has not already been scheduled.
- Future<bool> _commitExistsInDatastore(Commit commit) async {
- try {
- await datastore.db.lookupValue<Commit>(commit.key);
- } on KeyNotFoundException {
- return false;
- }
- return true;
- }
-
- /// Process and filters ciyaml.
- Future<CiYaml> getCiYaml(
- Commit commit, {
- bool validate = false,
- }) async {
- final Commit totCommit = await generateTotCommit(slug: commit.slug, branch: Config.defaultBranch(commit.slug));
- final CiYaml totYaml = await _getCiYaml(totCommit);
- return _getCiYaml(commit, totCiYaml: totYaml, validate: validate);
- }
-
- /// Load in memory the `.ci.yaml`.
- Future<CiYaml> _getCiYaml(
- Commit commit, {
- CiYaml? totCiYaml,
- bool validate = false,
- RetryOptions retryOptions = const RetryOptions(delayFactor: Duration(seconds: 2), maxAttempts: 4),
- }) async {
- String ciPath;
- ciPath = '${commit.repository}/${commit.sha!}/$kCiYamlPath';
- final Uint8List ciYamlBytes = (await cache.getOrCreate(
- subcacheName,
- ciPath,
- createFn: () => _downloadCiYaml(
- commit,
- ciPath,
- retryOptions: retryOptions,
- ),
- ttl: const Duration(hours: 1),
- ))!;
- final pb.SchedulerConfig schedulerConfig = pb.SchedulerConfig.fromBuffer(ciYamlBytes);
- log.fine('Retrieved .ci.yaml for $ciPath');
- // If totCiYaml is not null, we assume upper level function has verified that current branch is not a release branch.
- return CiYaml(
- config: schedulerConfig,
- slug: commit.slug,
- branch: commit.branch!,
- totConfig: totCiYaml,
- validate: validate,
- );
- }
-
- /// Get `.ci.yaml` from GitHub, and store the bytes in redis for future retrieval.
- ///
- /// If GitHub returns [HttpStatus.notFound], an empty config will be inserted assuming
- /// that commit does not support the scheduler config file.
- Future<Uint8List> _downloadCiYaml(
- Commit commit,
- String ciPath, {
- RetryOptions retryOptions = const RetryOptions(maxAttempts: 3),
- }) async {
- final String configContent = await githubFileContent(
- commit.slug,
- '.ci.yaml',
- httpClientProvider: httpClientProvider,
- ref: commit.sha!,
- retryOptions: retryOptions,
- );
- final YamlMap configYaml = loadYaml(configContent) as YamlMap;
- final pb.SchedulerConfig schedulerConfig = pb.SchedulerConfig()..mergeFromProto3Json(configYaml);
- return schedulerConfig.writeToBuffer();
- }
-
- /// Cancel all incomplete targets against a pull request.
- Future<void> cancelPreSubmitTargets({
- required github.PullRequest pullRequest,
- String reason = 'Newer commit available',
- }) async {
- await luciBuildService.cancelBuilds(pullRequest, reason);
- }
-
- /// Schedule presubmit targets against a pull request.
- ///
- /// Cancels all existing targets then schedules the targets.
- ///
- /// Schedules a [kCiYamlCheckName] to validate [CiYaml] is valid and all builds were able to be triggered.
- /// If [builderTriggerList] is specified, then trigger only those targets.
- Future<void> triggerPresubmitTargets({
- required github.PullRequest pullRequest,
- String reason = 'Newer commit available',
- List<String>? builderTriggerList,
- }) async {
- // Always cancel running builds so we don't ever schedule duplicates.
- log.info('Attempting to cancel existing presubmit targets for ${pullRequest.number}');
- await cancelPreSubmitTargets(
- pullRequest: pullRequest,
- reason: reason,
- );
-
- log.info('Creating ciYaml validation check run for ${pullRequest.number}');
- final github.CheckRun ciValidationCheckRun = await githubChecksService.githubChecksUtil.createCheckRun(
- config,
- pullRequest.base!.repo!.slug(),
- pullRequest.head!.sha!,
- kCiYamlCheckName,
- output: const github.CheckRunOutput(
- title: kCiYamlCheckName,
- summary: 'If this check is stuck pending, push an empty commit to retrigger the checks',
- ),
- );
-
- log.info('Creating presubmit targets for ${pullRequest.number}');
- final github.RepositorySlug slug = pullRequest.base!.repo!.slug();
- dynamic exception;
- try {
- // Both the author and label should be checked to make sure that no one is
- // attempting to get a pull request without check through.
- if (pullRequest.user!.login == config.autosubmitBot &&
- pullRequest.labels!.any((element) => element.name == Config.revertOfLabel)) {
- log.info('Skipping generating the full set of checks for revert request.');
- } else {
- final List<Target> presubmitTargets = await getPresubmitTargets(pullRequest);
- final List<Target> presubmitTriggerTargets = getTriggerList(presubmitTargets, builderTriggerList);
- await luciBuildService.scheduleTryBuilds(
- targets: presubmitTriggerTargets,
- pullRequest: pullRequest,
- );
- }
- } on FormatException catch (error, backtrace) {
- log.warning('FormatException encountered when scheduling presubmit targets for ${pullRequest.number}');
- log.warning(backtrace.toString());
- exception = error;
- } catch (error, backtrace) {
- log.warning('Exception encountered when scheduling presubmit targets for ${pullRequest.number}');
- log.warning(backtrace.toString());
- exception = error;
- }
-
- // Update validate ci.yaml check
- log.info('Updating ci.yaml validation check for ${pullRequest.number}');
- if (exception == null) {
- // Success in validating ci.yaml
- log.info('ci.yaml validation check was successful for ${pullRequest.number}');
- await githubChecksService.githubChecksUtil.updateCheckRun(
- config,
- slug,
- ciValidationCheckRun,
- status: github.CheckRunStatus.completed,
- conclusion: github.CheckRunConclusion.success,
- );
- } else {
- log.warning('Marking PR #${pullRequest.number} $kCiYamlCheckName as failed');
- log.warning(exception.toString());
- // Failure when validating ci.yaml
- await githubChecksService.githubChecksUtil.updateCheckRun(
- config,
- slug,
- ciValidationCheckRun,
- status: github.CheckRunStatus.completed,
- conclusion: github.CheckRunConclusion.failure,
- output: github.CheckRunOutput(
- title: kCiYamlCheckName,
- summary: '.ci.yaml has failures',
- text: exception.toString(),
- ),
- );
- }
- log.info(
- 'Finished triggering builds for: pr ${pullRequest.number}, commit ${pullRequest.base!.sha}, branch ${pullRequest.head!.ref} and slug ${pullRequest.base!.repo!.slug()}}',
- );
- }
-
- /// If [builderTriggerList] is specificed, return only builders that are contained in [presubmitTarget].
- /// Otherwise, return [presubmitTarget].
- List<Target> getTriggerList(List<Target> presubmitTarget, List<String>? builderTriggerList) {
- if (builderTriggerList != null && builderTriggerList.isNotEmpty) {
- return presubmitTarget.where((Target target) => builderTriggerList.contains(target.value.name)).toList();
- }
- return presubmitTarget;
- }
-
- /// Given a pull request event, retry all failed LUCI checks.
- ///
- /// 1. Aggregate .ci.yaml and try_builders.json presubmit builds.
- /// 2. Get failed LUCI builds for this pull request at [commitSha].
- /// 3. Rerun the failed builds that also have a failed check status.
- Future<void> retryPresubmitTargets({
- required github.PullRequest pullRequest,
- required CheckSuiteEvent checkSuiteEvent,
- }) async {
- final github.GitHub githubClient = await config.createGitHubClient(pullRequest: pullRequest);
- final Map<String, github.CheckRun> checkRuns = await githubChecksService.githubChecksUtil.allCheckRuns(
- githubClient,
- checkSuiteEvent,
- );
- final List<Target> presubmitTargets = await getPresubmitTargets(pullRequest);
- final List<Build?> failedBuilds = await luciBuildService.failedBuilds(pullRequest, presubmitTargets);
- for (Build? build in failedBuilds) {
- final github.CheckRun checkRun = checkRuns[build!.builderId.builder!]!;
-
- if (checkRun.status != github.CheckRunStatus.completed) {
- // Check run is still in progress, do not retry.
- continue;
- }
-
- await luciBuildService.scheduleTryBuilds(
- targets: presubmitTargets.where((Target target) => build.builderId.builder == target.value.name).toList(),
- pullRequest: pullRequest,
- checkSuiteEvent: checkSuiteEvent,
- );
- }
- }
-
- /// Get LUCI presubmit builders from .ci.yaml.
- ///
- /// Filters targets with runIf, matching them to the diff of [pullRequest].
- ///
- /// In the case there is an issue getting the diff from GitHub, all targets are returned.
- Future<List<Target>> getPresubmitTargets(github.PullRequest pullRequest) async {
- final Commit commit = Commit(
- branch: pullRequest.base!.ref,
- repository: pullRequest.base!.repo!.fullName,
- sha: pullRequest.head!.sha,
- );
- late CiYaml ciYaml;
- log.info('Attempting to read presubmit targets from ci.yaml for ${pullRequest.number}');
- if (commit.branch == Config.defaultBranch(commit.slug)) {
- ciYaml = await getCiYaml(commit, validate: true);
- } else {
- ciYaml = await getCiYaml(commit);
- }
- log.info('ci.yaml loaded successfully.');
- log.info('Collecting presubmit targets for ${pullRequest.number}');
-
- // Filter out schedulers targets with schedulers different than luci or cocoon.
- final Iterable<Target> presubmitTargets = ciYaml.presubmitTargets.where(
- (Target target) =>
- target.value.scheduler == pb.SchedulerSystem.luci || target.value.scheduler == pb.SchedulerSystem.cocoon,
- );
-
- log.info('Collected ${presubmitTargets.length} presubmit targets.');
- // Release branches should run every test.
- if (pullRequest.base!.ref != Config.defaultBranch(pullRequest.base!.repo!.slug())) {
- log.info('Release branch found, scheduling all targets for ${pullRequest.number}');
- return presubmitTargets.toList();
- }
-
- // Filter builders based on the PR diff
- final GithubService githubService = await config.createGithubService(commit.slug);
- List<String> files = <String>[];
- try {
- files = await githubService.listFiles(pullRequest);
- } on github.GitHubError catch (error) {
- log.warning(error);
- log.warning('Unable to get diff for pullRequest=$pullRequest');
- log.warning('Running all targets');
- return presubmitTargets.toList();
- }
- return getTargetsToRun(presubmitTargets, files);
- }
-
- /// Reschedules a failed build using a [CheckRunEvent]. The CheckRunEvent is
- /// generated when someone clicks the re-run button from a failed build from
- /// the Github UI.
- ///
- /// If the rerequested check is for [kCiYamlCheckName], all presubmit jobs are retried.
- /// Otherwise, the specific check will be retried.
- ///
- /// Relevant APIs:
- /// https://developer.github.com/v3/checks/runs/#check-runs-and-requested-actions
- Future<bool> processCheckRun(cocoon_checks.CheckRunEvent checkRunEvent) async {
- switch (checkRunEvent.action) {
- case 'rerequested':
- log.fine('Rerun requested by GitHub user: ${checkRunEvent.sender?.login}');
- final String? name = checkRunEvent.checkRun!.name;
- bool success = false;
- if (name == kCiYamlCheckName) {
- // The CheckRunEvent.checkRun.pullRequests array is empty for this
- // event, so we need to find the matching pull request.
- final RepositorySlug slug = checkRunEvent.repository!.slug();
- final String headSha = checkRunEvent.checkRun!.headSha!;
- final int checkSuiteId = checkRunEvent.checkRun!.checkSuite!.id!;
- final PullRequest? pullRequest =
- await githubChecksService.findMatchingPullRequest(slug, headSha, checkSuiteId);
- if (pullRequest != null) {
- log.fine('Matched PR: ${pullRequest.number} Repo: ${slug.fullName}');
- await triggerPresubmitTargets(pullRequest: pullRequest);
- success = true;
- } else {
- log.warning('No matching PR found for head_sha in check run event.');
- }
- } else {
- try {
- final RepositorySlug slug = checkRunEvent.repository!.slug();
- final String gitBranch = checkRunEvent.checkRun!.checkSuite!.headBranch ?? Config.defaultBranch(slug);
- final String sha = checkRunEvent.checkRun!.headSha!;
-
- // Only merged commits are added to the datastore. If a matching commit is found, this must be a postsubmit checkrun.
- datastore = datastoreProvider(config.db);
- final Key<String> commitKey =
- Commit.createKey(db: datastore.db, slug: slug, gitBranch: gitBranch, sha: sha);
- Commit? commit;
- try {
- commit = await Commit.fromDatastore(datastore: datastore, key: commitKey);
- log.fine('Commit found in datastore.');
- } on KeyNotFoundException {
- log.fine('Commit not found in datastore.');
- }
-
- if (commit == null) {
- log.fine('Rescheduling presubmit build.');
- await luciBuildService.reschedulePresubmitBuildUsingCheckRunEvent(checkRunEvent);
- } else {
- log.fine('Rescheduling postsubmit build.');
- final String checkName = checkRunEvent.checkRun!.name!;
- final Task task = await Task.fromDatastore(datastore: datastore, commitKey: commitKey, name: checkName);
- final CiYaml ciYaml = await getCiYaml(commit);
- final Target target =
- ciYaml.postsubmitTargets.singleWhere((Target target) => target.value.name == task.name);
- await luciBuildService.reschedulePostsubmitBuildUsingCheckRunEvent(
- checkRunEvent,
- commit: commit,
- task: task,
- target: target,
- );
- }
-
- success = true;
- } on NoBuildFoundException {
- log.warning('No build found to reschedule.');
- }
- }
-
- log.fine('CheckName: $name State: $success');
- return success;
- }
-
- return true;
- }
-
- /// Push [Commit] to BigQuery as part of the infra metrics dashboards.
- Future<void> _uploadToBigQuery(Commit commit) async {
- const String projectId = 'flutter-dashboard';
- const String dataset = 'cocoon';
- const String table = 'Checklist';
-
- log.info('Uploading commit ${commit.sha} info to bigquery.');
-
- final TabledataResource tabledataResource = await config.createTabledataResourceApi();
- final List<Map<String, Object>> tableDataInsertAllRequestRows = <Map<String, Object>>[];
-
- /// Consolidate [commits] together
- ///
- /// Prepare for bigquery [insertAll]
- tableDataInsertAllRequestRows.add(<String, Object>{
- 'json': <String, Object?>{
- 'ID': commit.id,
- 'CreateTimestamp': commit.timestamp,
- 'FlutterRepositoryPath': commit.repository,
- 'CommitSha': commit.sha!,
- 'CommitAuthorLogin': commit.author,
- 'CommitAuthorAvatarURL': commit.authorAvatarUrl,
- 'CommitMessage': commit.message,
- 'Branch': commit.branch,
- },
- });
-
- /// Final [rows] to be inserted to [BigQuery]
- final TableDataInsertAllRequest rows =
- TableDataInsertAllRequest.fromJson(<String, Object>{'rows': tableDataInsertAllRequestRows});
-
- /// Insert [commits] to [BigQuery]
- try {
- if (rows.rows == null) {
- log.warning('Rows to be inserted is null');
- } else {
- log.info('Inserting ${rows.rows!.length} into big query for ${commit.sha}');
- }
- await tabledataResource.insertAll(rows, projectId, dataset, table);
- } on ApiRequestError {
- log.warning('Failed to add commits to BigQuery: $ApiRequestError');
- }
- }
-
- /// Returns the tip of tree [Commit] using specified [branch] and [RepositorySlug].
- ///
- /// A tip of tree [Commit] is used to help generate the tip of tree [CiYaml].
- /// The generated tip of tree [CiYaml] will be compared against Presubmit Targets in current [CiYaml],
- /// to ensure new targets without `bringup: true` label are not added into the build.
- Future<Commit> generateTotCommit({required String branch, required RepositorySlug slug}) async {
- datastore = datastoreProvider(config.db);
- final BuildStatusService buildStatusService = buildStatusProvider(datastore);
- final Commit totCommit = (await buildStatusService
- .retrieveCommitStatus(
- limit: 1,
- branch: branch,
- slug: slug,
- )
- .map<Commit>((CommitStatus status) => status.commit)
- .toList())
- .single;
-
- return totCommit;
- }
-}
diff --git a/app_dart/lib/src/service/scheduler/policy.dart b/app_dart/lib/src/service/scheduler/policy.dart
deleted file mode 100644
index 62f3538..0000000
--- a/app_dart/lib/src/service/scheduler/policy.dart
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright 2021 The Flutter 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 'package:cocoon_service/src/service/datastore.dart';
-
-import '../../model/appengine/task.dart';
-import '../logging.dart';
-import '../luci_build_service.dart';
-
-/// Interface for implementing various scheduling policies in the Cocoon scheduler.
-abstract class SchedulerPolicy {
- /// Returns the priority of [Task].
- ///
- /// If null is returned, the task should not be scheduled.
- Future<int?> triggerPriority({
- required Task task,
- required DatastoreService datastore,
- });
-}
-
-/// Every [Task] is triggered to run.
-class GuaranteedPolicy implements SchedulerPolicy {
- @override
- Future<int?> triggerPriority({
- required Task task,
- required DatastoreService datastore,
- }) async {
- final List<Task> recentTasks = await datastore.queryRecentTasksByName(name: task.name!).toList();
- // Ensure task isn't considered in recentTasks
- recentTasks.removeWhere((Task t) => t.commitKey == task.commitKey);
- if (recentTasks.isEmpty) {
- log.warning('${task.name} is newly added, triggerring builds regardless of policy');
- return LuciBuildService.kDefaultPriority;
- }
- // Prioritize tasks that recently failed.
- if (shouldRerunPriority(recentTasks, 1)) {
- return LuciBuildService.kRerunPriority;
- }
- return LuciBuildService.kDefaultPriority;
- }
-}
-
-/// [Task] is run at least every 6 commits.
-///
-/// If there is capacity, a backfiller cron triggers the latest task that was not run
-/// to ensure ToT is always tested.
-///
-/// This is intended for targets that are run in an infra pool that has limited capacity,
-/// such as the on device tests in the DeviceLab.
-class BatchPolicy implements SchedulerPolicy {
- static const int kBatchSize = 6;
- @override
- Future<int?> triggerPriority({
- required Task task,
- required DatastoreService datastore,
- }) async {
- final List<Task> recentTasks = await datastore.queryRecentTasksByName(name: task.name!).toList();
- // Skip scheduling if there is already a running task.
- if (recentTasks.any((Task task) => task.status == Task.statusInProgress)) {
- return null;
- }
-
- // Ensure task isn't considered in recentTasks
- recentTasks.removeWhere((Task t) => t.commitKey == task.commitKey);
- if (recentTasks.length < kBatchSize) {
- log.warning('${task.name} has less than $kBatchSize, skip scheduling to wait for ci.yaml roll.');
- return null;
- }
-
- // Prioritize tasks that recently failed.
- if (shouldRerunPriority(recentTasks, kBatchSize)) {
- return LuciBuildService.kRerunPriority;
- }
-
- if (allNew(recentTasks.sublist(0, kBatchSize - 1))) {
- return LuciBuildService.kDefaultPriority;
- }
-
- return null;
- }
-}
-
-/// Checks if all tasks are with [Task.statusNew].
-bool allNew(List<Task> tasks) {
- for (Task task in tasks) {
- if (task.status != Task.statusNew) {
- return false;
- }
- }
- return true;
-}
-
-/// Return true if there is an earlier failed build.
-bool shouldRerunPriority(List<Task> tasks, int pastTaskNumber) {
- // Prioritize tasks that recently failed.
- bool hasRecentFailure = false;
- for (int i = 0; i < pastTaskNumber && i < tasks.length; i++) {
- if (_isFailed(tasks[i])) {
- hasRecentFailure = true;
- break;
- }
- }
- return hasRecentFailure;
-}
-
-bool _isFailed(Task task) {
- return task.status == Task.statusFailed || task.status == Task.statusInfraFailure;
-}
-
-/// [Task] run outside of Cocoon are not triggered by the Cocoon scheduler.
-class OmitPolicy implements SchedulerPolicy {
- @override
- Future<int?> triggerPriority({
- required Task task,
- required DatastoreService datastore,
- }) async =>
- null;
-}
diff --git a/app_dart/pubspec.yaml b/app_dart/pubspec.yaml
deleted file mode 100644
index c6f9fc7..0000000
--- a/app_dart/pubspec.yaml
+++ /dev/null
@@ -1,55 +0,0 @@
-# Copyright 2019 The Flutter Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-name: cocoon_service
-description: AppEngine service for managing Flutter CI
-homepage: https://github.com/flutter/cocoon
-publish_to: none
-
-environment:
- sdk: '>=3.0.0-0 <4.0.0'
-
-dependencies:
- appengine: 0.13.7
- args: 2.4.2
- collection: 1.18.0
- corsac_jwt: 1.0.0-nullsafety.1
- crypto: 3.0.3
- dbcrypt: 2.0.0
- file: 7.0.0
- fixnum: 1.1.0
- gcloud: 0.8.11
- github: 9.19.0
- googleapis: 11.4.0
- googleapis_auth: 1.4.1
- gql: 1.0.1-alpha+1696717343881
- graphql: 5.2.0-beta.6
- grpc: 3.2.4
- http: 1.1.0
- json_annotation: 4.8.1
- logging: 1.2.0
- meta: 1.11.0
- mime: 1.0.4
- mutex: 3.1.0
- neat_cache: 2.0.3
- path: 1.8.3
- process: 5.0.1
- process_runner: 4.1.4
- protobuf: 2.1.0
- retry: ^3.1.2
- truncate: 3.0.1
- yaml: 3.1.2
-
-dev_dependencies:
- analyzer: 5.13.0
- build_runner: 2.4.6
- fake_async: 1.3.1
- flutter_lints: 3.0.1
- json_serializable: 6.7.1
- mockito: 5.4.2
- platform: 3.1.3
- test: 1.24.9
-
-builders:
- json_serializable: 3.3.0
diff --git a/app_dart/test/foundation/utils_test.dart b/app_dart/test/foundation/utils_test.dart
deleted file mode 100644
index 9af49b4..0000000
--- a/app_dart/test/foundation/utils_test.dart
+++ /dev/null
@@ -1,392 +0,0 @@
-// Copyright 2020 The Flutter 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:convert';
-import 'dart:io';
-
-import 'package:cocoon_service/src/foundation/utils.dart';
-import 'package:cocoon_service/src/model/ci_yaml/target.dart';
-import 'package:cocoon_service/src/service/logging.dart';
-import 'package:github/github.dart';
-import 'package:googleapis/bigquery/v2.dart';
-import 'package:http/http.dart' as http;
-import 'package:http/testing.dart';
-import 'package:logging/logging.dart';
-import 'package:retry/retry.dart';
-import 'package:test/test.dart';
-
-import '../src/bigquery/fake_tabledata_resource.dart';
-import '../src/utilities/entity_generators.dart';
-
-const String branchRegExp = '''
- master
- flutter-1.1-candidate.1
- ''';
-const String luciBuilders = '''
- {
- "builders":[
- {
- "name":"Cocoon",
- "repo":"cocoon",
- "enabled":true
- }, {
- "name":"Cocoon2",
- "repo":"cocoon",
- "enabled":false
- }
- ]
- }
- ''';
-
-void main() {
- group('Test utils', () {
- const RetryOptions noRetry = RetryOptions(
- maxAttempts: 1,
- delayFactor: Duration.zero,
- maxDelay: Duration.zero,
- );
- group('githubFileContent', () {
- late MockClient branchHttpClient;
-
- test('returns branches', () async {
- branchHttpClient = MockClient((_) async => http.Response(branchRegExp, HttpStatus.ok));
- final String branches = await githubFileContent(
- RepositorySlug('flutter', 'cocoon'),
- 'branches.txt',
- httpClientProvider: () => branchHttpClient,
- retryOptions: noRetry,
- );
- final List<String> branchList = branches.split('\n').map((String branch) => branch.trim()).toList();
- branchList.removeWhere((String branch) => branch.isEmpty);
- expect(branchList, <String>['master', 'flutter-1.1-candidate.1']);
- });
-
- test('retries branches download upon HTTP failure', () async {
- int retry = 0;
- branchHttpClient = MockClient((_) async {
- if (retry++ == 0) {
- return http.Response('', HttpStatus.serviceUnavailable);
- }
- return http.Response(branchRegExp, HttpStatus.ok);
- });
- final List<LogRecord> records = <LogRecord>[];
- log.onRecord.listen((LogRecord record) => records.add(record));
- final String branches = await githubFileContent(
- RepositorySlug('flutter', 'cocoon'),
- 'branches.txt',
- httpClientProvider: () => branchHttpClient,
- retryOptions: const RetryOptions(
- maxAttempts: 3,
- delayFactor: Duration.zero,
- maxDelay: Duration.zero,
- ),
- );
- final List<String> branchList = branches.split('\n').map((String branch) => branch.trim()).toList();
- branchList.removeWhere((String branch) => branch.isEmpty);
- expect(retry, 2);
- expect(branchList, <String>['master', 'flutter-1.1-candidate.1']);
- expect(records.where((LogRecord record) => record.level == Level.INFO), isNotEmpty);
- expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty);
- });
-
- test('falls back to git on borg', () async {
- branchHttpClient = MockClient((http.Request request) async {
- if (request.url.toString() ==
- 'https://flutter.googlesource.com/mirrors/cocoon/+/ba7fe03781762603a1cdc364f8f5de56a0fdbf5c/.ci.yaml?format=text') {
- return http.Response(base64Encode(branchRegExp.codeUnits), HttpStatus.ok);
- }
- // Mock a GitHub outage
- return http.Response('', HttpStatus.serviceUnavailable);
- });
- final List<LogRecord> records = <LogRecord>[];
- log.onRecord.listen((LogRecord record) => records.add(record));
- final String branches = await githubFileContent(
- RepositorySlug('flutter', 'cocoon'),
- '.ci.yaml',
- httpClientProvider: () => branchHttpClient,
- ref: 'ba7fe03781762603a1cdc364f8f5de56a0fdbf5c',
- retryOptions: const RetryOptions(
- maxAttempts: 1,
- delayFactor: Duration.zero,
- maxDelay: Duration.zero,
- ),
- );
- final List<String> branchList = branches.split('\n').map((String branch) => branch.trim()).toList();
- branchList.removeWhere((String branch) => branch.isEmpty);
- expect(branchList, <String>['master', 'flutter-1.1-candidate.1']);
- });
-
- test('falls back to git on borg when given sha', () async {
- branchHttpClient = MockClient((http.Request request) async {
- if (request.url.toString() ==
- 'https://flutter.googlesource.com/mirrors/cocoon/+/refs/heads/main/.ci.yaml?format=text') {
- return http.Response(base64Encode(branchRegExp.codeUnits), HttpStatus.ok);
- }
- // Mock a GitHub outage
- return http.Response('', HttpStatus.serviceUnavailable);
- });
- final List<LogRecord> records = <LogRecord>[];
- log.onRecord.listen((LogRecord record) => records.add(record));
- final String branches = await githubFileContent(
- RepositorySlug('flutter', 'cocoon'),
- '.ci.yaml',
- ref: 'main',
- httpClientProvider: () => branchHttpClient,
- retryOptions: const RetryOptions(
- maxAttempts: 1,
- delayFactor: Duration.zero,
- maxDelay: Duration.zero,
- ),
- );
- final List<String> branchList = branches.split('\n').map((String branch) => branch.trim()).toList();
- branchList.removeWhere((String branch) => branch.isEmpty);
- expect(branchList, <String>['master', 'flutter-1.1-candidate.1']);
- });
-
- test('gives up after 6 tries', () async {
- int retry = 0;
- branchHttpClient = MockClient((_) async {
- retry++;
- return http.Response('', HttpStatus.serviceUnavailable);
- });
- final List<LogRecord> records = <LogRecord>[];
- log.onRecord.listen((LogRecord record) => records.add(record));
- await expectLater(
- githubFileContent(
- RepositorySlug('flutter', 'cocoon'),
- 'branches.txt',
- httpClientProvider: () => branchHttpClient,
- retryOptions: const RetryOptions(
- maxAttempts: 3,
- delayFactor: Duration.zero,
- maxDelay: Duration.zero,
- ),
- ),
- throwsA(isA<HttpException>()),
- );
- // It will request from GitHub 3 times, fallback to GoB, then fail.
- expect(retry, 6);
- expect(records.where((LogRecord record) => record.level == Level.WARNING), isNotEmpty);
- });
- });
-
- group('GitHubBackoffCalculator', () {
- test('twoSecondLinearBackoff', () {
- expect(twoSecondLinearBackoff(0), const Duration(seconds: 2));
- expect(twoSecondLinearBackoff(1), const Duration(seconds: 4));
- expect(twoSecondLinearBackoff(2), const Duration(seconds: 6));
- expect(twoSecondLinearBackoff(3), const Duration(seconds: 8));
- });
- });
-
- group('bigquery', () {
- late FakeTabledataResource tabledataResourceApi;
-
- setUp(() {
- tabledataResourceApi = FakeTabledataResource();
- });
- test('Insert data to bigquery', () async {
- await insertBigquery('test', <String, dynamic>{'test': 'test'}, tabledataResourceApi);
- final TableDataList tableDataList = await tabledataResourceApi.list('test', 'test', 'test');
- expect(tableDataList.totalRows, '1');
- });
- });
-
- group('getFilteredBuilders', () {
- test('does not return builders when run_if does not match any file', () async {
- final List<Target> targets = <Target>[
- generateTarget(1, runIf: <String>['cde/']),
- ];
- final List<String> files = <String>['abc/cde.py', 'cde/fgh.dart'];
- final List<Target> result = await getTargetsToRun(targets, files);
- expect(result.isEmpty, isTrue);
- });
-
- test('returns builders when run_if is null', () async {
- final List<String> files = <String>['abc/def.py', 'cde/dgh.dart'];
- final List<Target> targets = <Target>[generateTarget(1)];
- final List<Target> result = await getTargetsToRun(targets, files);
- expect(result, targets);
- });
-
- test('returns builders when run_if matches files using full path', () async {
- final List<String> files = <String>['abc/cde.py', 'cgh/dhj.dart'];
- final List<Target> targets = <Target>[
- generateTarget(1, runIf: <String>['abc/cde.py']),
- ];
- final List<Target> result = await getTargetsToRun(targets, files);
- expect(result, targets);
- });
-
- test('returns builders when run_if matches files with **', () async {
- final List<Target> targets = <Target>[
- generateTarget(1, runIf: <String>['abc/**']),
- ];
- final List<String> files = <String>['abc/cdf/hj.dart', 'abc/dej.dart'];
- final List<Target> result = await getTargetsToRun(targets, files);
- expect(result, targets);
- });
-
- test('returns builders when run_if matches files with ** that contain digits', () async {
- final List<Target> targets = <Target>[
- generateTarget(
- 1,
- runIf: <String>[
- 'dev/**',
- 'packages/flutter/**',
- 'packages/flutter_driver/**',
- 'packages/integration_test/**',
- 'packages/flutter_localizations/**',
- 'packages/fuchsia_remote_debug_protocol/**',
- 'packages/flutter_test/**',
- 'packages/flutter_goldens/**',
- 'packages/flutter_tools/**',
- 'bin/**',
- '.ci.yaml',
- ],
- ),
- ];
- final List<String> files = <String>[
- 'packages/flutter_localizations/lib/src/l10n/material_es.arb',
- 'packages/flutter_localizations/lib/src/l10n/material_en_ZA.arb',
- ];
- final List<Target> result = await getTargetsToRun(targets, files);
- expect(result, targets);
- });
-
- test('returns builders when run_if matches files with * and ** that contains digits', () async {
- final List<Target> targets = <Target>[
- generateTarget(
- 1,
- runIf: <String>[
- 'dev/**',
- 'packages/flutter/**',
- 'packages/flutter_driver/**',
- 'packages/integration_test/**',
- 'packages/flutter_localizations/**/l10n/cupertino*.arb',
- 'packages/fuchsia_remote_debug_protocol/**',
- 'packages/flutter_test/**',
- 'packages/flutter_goldens/**',
- 'packages/flutter_tools/**',
- 'bin/**',
- '.ci.yaml',
- ],
- ),
- ];
- final List<String> files = <String>[
- 'packages/flutter_localizations/lib/src/l10n/material_es.arb',
- 'packages/flutter_localizations/lib/src/l10n/material_en_ZA.arb',
- 'packages/flutter_localizations/lib/src/l10n/cupertino_cy.arb',
- ];
- final List<Target> result = await getTargetsToRun(targets, files);
- expect(result, targets);
- });
-
- test('returns builders when run_if matches files with * trailing glob', () async {
- final List<Target> targets = <Target>[
- generateTarget(
- 1,
- runIf: <String>[
- 'packages/flutter_localizations/**/l10n/*',
- ],
- ),
- ];
- final List<String> files = <String>[
- 'packages/flutter_localizations/lib/src/l10n/material_es.arb',
- 'packages/flutter_localizations/lib/src/l10n/material_en_ZA.arb',
- 'packages/flutter_localizations/lib/src/l10n/cupertino_cy.arb',
- ];
- final List<Target> result = await getTargetsToRun(targets, files);
- expect(result, targets);
- });
-
- test('returns builders when run_if matches files with * trailing glob 2', () async {
- final List<Target> targets = <Target>[
- generateTarget(
- 1,
- runIf: <String>[
- 'packages/flutter_localizations/**/l10n/cupertino*',
- ],
- ),
- ];
- final List<String> files = <String>[
- 'packages/flutter_localizations/lib/src/l10n/material_es.arb',
- 'packages/flutter_localizations/lib/src/l10n/material_en_ZA.arb',
- 'packages/flutter_localizations/lib/src/l10n/cupertino_cy.arb',
- ];
- final List<Target> result = await getTargetsToRun(targets, files);
- expect(result, targets);
- });
-
- test('returns builders when run_if matches files with ** in the middle', () async {
- final List<Target> targets = <Target>[
- generateTarget(1, runIf: <String>['abc/**/hj.dart']),
- ];
- final List<String> files = <String>['abc/cdf/efg/hj.dart', 'abc/dej.dart'];
- final List<Target> result = await getTargetsToRun(targets, files);
- expect(result, [targets[0]]);
- });
-
- test('returns builders when run_if matches files with both * and **', () async {
- final List<Target> targets = <Target>[
- generateTarget(1, runIf: <String>['a/b*c/**']),
- ];
- final List<String> files = <String>['a/baddsc/defg.zz', 'c/d'];
- final List<Target> result = await getTargetsToRun(targets, files);
- expect(result, targets);
- });
-
- test('returns correct builders when file and folder share the same name', () async {
- final List<Target> targets = <Target>[
- generateTarget(1, runIf: <String>['a/b/']),
- generateTarget(2, runIf: <String>['a']),
- ];
- final List<String> files = <String>['a'];
- final List<Target> result = await getTargetsToRun(targets, files);
- expect(result.length, 1);
- expect(result.single, targets[1]);
- });
-
- test('run_if takes precedence over run_if_not', () async {
- final List<Target> targets = <Target>[
- generateTarget(1, runIf: <String>['a/b/']),
- generateTarget(2, runIf: <String>['a'], runIfNot: <String>['a']),
- ];
- final List<String> files = <String>['a'];
- final List<Target> result = await getTargetsToRun(targets, files);
- expect(result.length, 1);
- expect(result.single, targets[1]);
- });
-
- test('no run_if and not run_if_not', () async {
- final List<Target> targets = <Target>[
- generateTarget(1),
- ];
- final List<String> files = <String>['a'];
- final List<Target> result = await getTargetsToRun(targets, files);
- expect(result.length, 1);
- expect(result.single, targets[0]);
- });
-
- test('run_if_not with matches', () async {
- final List<Target> targets = <Target>[
- generateTarget(1, runIfNot: ['/a/b/**']),
- ];
- final List<String> files = <String>['/a/b/c/d'];
- final List<Target> result = await getTargetsToRun(targets, files);
- expect(result.length, 0);
- });
-
- test('run_if_not with no matches', () async {
- final List<Target> targets = <Target>[
- generateTarget(1, runIfNot: ['/a/b/**']),
- ];
- final List<String> files = <String>['/a/c'];
- final List<Target> result = await getTargetsToRun(targets, files);
- expect(result.length, 1);
- expect(result.single, targets[0]);
- });
- });
- });
-}
diff --git a/app_dart/test/model/buildbucket_test.dart b/app_dart/test/model/buildbucket_test.dart
deleted file mode 100644
index 704f3a1..0000000
--- a/app_dart/test/model/buildbucket_test.dart
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 2019 The Flutter 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:convert';
-
-import 'package:cocoon_service/src/model/common/json_converters.dart';
-import 'package:cocoon_service/src/model/luci/buildbucket.dart';
-import 'package:test/test.dart';
-
-void main() {
- const String tagsJson = '['
- '{"key":"tag_a","value":"chrome/win32-builder-perf"},'
- '{"key":"tag_b","value":"true"},'
- '{"key":"tag_b","value":"9083774268329986752"}'
- ']';
-
- const Map<String, List<String>> tags = <String, List<String>>{
- 'tag_a': <String>['chrome/win32-builder-perf'],
- 'tag_b': <String>['true', '9083774268329986752'],
- };
-
- test('Deserializes tags', () {
- final List<dynamic>? decodedTags = json.decode(tagsJson) as List<dynamic>?;
- expect(const TagsConverter().fromJson(decodedTags), tags);
- });
-
- test('Serializes tags', () {
- final List<Map<String, String>> encodedTags =
- const TagsConverter().toJson(tags)!.cast<Map<String, String>>().toList();
- expect(encodedTags.length, 3);
- expect(json.encode(encodedTags), tagsJson);
- });
-
- test('Handles Build id correctly', () {
- final String id = 0xFFFFFFFFFFFFFFFF.toString(); // would overflow a 32 bit int
- final Build build = Build(id: id, builderId: const BuilderId());
- final Map<String, dynamic> buildJson = build.toJson();
- expect(buildJson['id'], id.toString());
- expect(buildJson['id'].runtimeType, String);
-
- final Build deserializedBuild = Build.fromJson(json.decode(json.encode(buildJson)) as Map<String, dynamic>);
- expect(deserializedBuild.id, id);
-
- final GetBuildRequest request = GetBuildRequest(id: id);
- final Map<String, dynamic> requestBuildJson = request.toJson();
- expect(requestBuildJson['id'], id.toString());
- expect(requestBuildJson['id'].runtimeType, String);
-
- final GetBuildRequest deserializedRequest = GetBuildRequest.fromJson(requestBuildJson);
- expect(deserializedRequest.id, id);
- });
-
- test('Handles fields correctly', () {
- GetBuildRequest request = const GetBuildRequest(id: '9083774268329986752');
- Map<String, dynamic> requestBuildJson = request.toJson();
- expect(requestBuildJson['id'], request.id.toString());
- request = const GetBuildRequest(id: '9083774268329986752', fields: 'summaryMarkDown');
- requestBuildJson = request.toJson();
- expect(requestBuildJson['id'], request.id.toString());
- expect(requestBuildJson['fields'], 'summaryMarkDown');
- });
-
- test('Creates a ScheduleBuildRequest', () {
- const ScheduleBuildRequest req = ScheduleBuildRequest(
- builderId: BuilderId(
- project: 'flutter',
- bucket: 'try',
- builder: 'fake_builder',
- ),
- properties: <String, String>{
- 'git_url': 'https://github.com/flutter/flutter',
- 'git_ref': 'refs/pull/63834/head',
- },
- dimensions: <RequestedDimension>[RequestedDimension(key: 'a', value: 'b', expiration: '120s')],
- priority: 100,
- );
- expect(
- json.encode(req.toJson()),
- '{"builder":{"project":"flutter","bucket":"try","builder":"fake_builder"},'
- '"properties":{"git_url":"https://github.com/flutter/flutter","git_ref":"refs/pull/63834/head"},'
- '"dimensions":[{"key":"a","value":"b","expiration":"120s"}],"priority":100}');
- });
-}
diff --git a/app_dart/test/model/ci_yaml/ci_yaml_test.dart b/app_dart/test/model/ci_yaml/ci_yaml_test.dart
deleted file mode 100644
index d927755..0000000
--- a/app_dart/test/model/ci_yaml/ci_yaml_test.dart
+++ /dev/null
@@ -1,346 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:cocoon_service/src/model/ci_yaml/ci_yaml.dart';
-import 'package:cocoon_service/src/model/ci_yaml/target.dart';
-import 'package:cocoon_service/protos.dart' as pb;
-import 'package:cocoon_service/src/service/config.dart';
-import 'package:test/test.dart';
-
-import '../../src/service/fake_scheduler.dart';
-
-void main() {
- group('enabledBranchesMatchesCurrentBranch', () {
- final List<EnabledBranchesRegexTest> tests = <EnabledBranchesRegexTest>[
- EnabledBranchesRegexTest('matches main', 'main', <String>['main']),
- EnabledBranchesRegexTest(
- 'matches candidate branch',
- 'flutter-2.4-candidate.3',
- <String>['flutter-\\d+\\.\\d+-candidate\\.\\d+'],
- ),
- EnabledBranchesRegexTest('matches main when not first pattern', 'main', <String>['dev', 'main']),
- EnabledBranchesRegexTest('does not do partial matches', 'super-main', <String>['main'], false),
- ];
-
- for (EnabledBranchesRegexTest regexTest in tests) {
- test(regexTest.name, () {
- expect(
- CiYaml.enabledBranchesMatchesCurrentBranch(regexTest.enabledBranches, regexTest.branch),
- regexTest.expectation,
- );
- });
- }
- });
-
- group('Validate pinned version operation.', () {
- void validatePinnedVersion(String input) {
- test('$input -> returns normally', () {
- DependencyValidator.hasVersion(dependencyJsonString: input);
- });
- }
-
- validatePinnedVersion('[{"dependency": "chrome_and_driver", "version": "version:96.2"}]');
- validatePinnedVersion('[{"dependency": "open_jdk", "version": "11"}]');
- validatePinnedVersion('[{"dependency": "android_sdk", "version": "version:31v8"}]');
- validatePinnedVersion(
- '[{"dependency": "goldctl", "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603"}]',
- );
- });
-
- group('Validate un-pinned version operation.', () {
- void validateUnPinnedVersion(String input) {
- test('$input -> returns normally', () {
- expect(() => DependencyValidator.hasVersion(dependencyJsonString: input), throwsException);
- });
- }
-
- validateUnPinnedVersion('[{"dependency": "some_sdk", "version": ""}]');
- validateUnPinnedVersion('[{"dependency": "another_sdk"}]');
- validateUnPinnedVersion('[{"dependency": "yet_another_sdk", "version": "latest"}]');
- });
-
- group('initialTargets', () {
- test('targets without deps', () {
- final CiYaml ciYaml = exampleConfig;
- final List<Target> initialTargets = ciYaml.getInitialTargets(ciYaml.postsubmitTargets);
- final List<String> initialTargetNames = initialTargets.map((Target target) => target.value.name).toList();
- expect(
- initialTargetNames,
- containsAll(
- <String>[
- 'Linux A',
- 'Mac A',
- 'Windows A',
- ],
- ),
- );
- });
-
- test('filter bringup targets on release branches', () {
- final CiYaml ciYaml = CiYaml(
- slug: Config.flutterSlug,
- branch: Config.defaultBranch(Config.flutterSlug),
- config: pb.SchedulerConfig(
- enabledBranches: <String>[
- Config.defaultBranch(Config.flutterSlug),
- ],
- targets: <pb.Target>[
- pb.Target(
- name: 'Linux A',
- ),
- pb.Target(
- name: 'Mac A', // Should be ignored on release branches
- bringup: true,
- ),
- ],
- ),
- );
- final List<Target> initialTargets = ciYaml.getInitialTargets(ciYaml.postsubmitTargets);
- final List<String> initialTargetNames = initialTargets.map((Target target) => target.value.name).toList();
- expect(
- initialTargetNames,
- containsAll(
- <String>[
- 'Linux A',
- ],
- ),
- );
- });
-
- group('validations and filters.', () {
- final CiYaml totCIYaml = CiYaml(
- slug: Config.flutterSlug,
- branch: Config.defaultBranch(Config.flutterSlug),
- config: pb.SchedulerConfig(
- enabledBranches: <String>[
- Config.defaultBranch(Config.flutterSlug),
- ],
- targets: <pb.Target>[
- pb.Target(
- name: 'Linux A',
- ),
- pb.Target(
- name: 'Mac A', // Should be ignored on release branches
- bringup: true,
- ),
- ],
- ),
- );
- final CiYaml ciYaml = CiYaml(
- slug: Config.flutterSlug,
- branch: 'flutter-2.4-candidate.3',
- config: pb.SchedulerConfig(
- enabledBranches: <String>[
- 'flutter-2.4-candidate.3',
- ],
- targets: <pb.Target>[
- pb.Target(
- name: 'Linux A',
- ),
- pb.Target(
- name: 'Linux B',
- ),
- pb.Target(
- name: 'Mac A', // Should be ignored on release branches
- bringup: true,
- ),
- ],
- ),
- totConfig: totCIYaml,
- );
-
- test('filter targets removed from presubmit', () {
- final List<Target> initialTargets = ciYaml.presubmitTargets;
- final List<String> initialTargetNames = initialTargets.map((Target target) => target.value.name).toList();
- expect(
- initialTargetNames,
- containsAll(
- <String>[
- 'Linux A',
- ],
- ),
- );
- });
-
- test('filter targets removed from postsubmit', () {
- final List<Target> initialTargets = ciYaml.postsubmitTargets;
- final List<String> initialTargetNames = initialTargets.map((Target target) => target.value.name).toList();
- expect(
- initialTargetNames,
- containsAll(
- <String>[
- 'Linux A',
- ],
- ),
- );
- });
-
- test('Get backfill targets from postsubmit', () {
- final CiYaml ciYaml = exampleBackfillConfig;
- final List<Target> backfillTargets = ciYaml.backfillTargets;
- final List<String> backfillTargetNames = backfillTargets.map((Target target) => target.value.name).toList();
- expect(
- backfillTargetNames,
- containsAll(
- <String>[
- 'Linux A',
- 'Mac A',
- ],
- ),
- );
- });
-
- test('filter release_build targets from release candidate branches', () {
- final CiYaml releaseYaml = CiYaml(
- slug: Config.flutterSlug,
- branch: 'flutter-2.4-candidate.3',
- config: pb.SchedulerConfig(
- enabledBranches: <String>[
- 'flutter-2.4-candidate.3',
- ],
- targets: <pb.Target>[
- pb.Target(
- name: 'Linux A',
- properties: <String, String>{'release_build': 'true'},
- ),
- pb.Target(
- name: 'Linux B',
- ),
- pb.Target(
- name: 'Mac A', // Should be ignored on release branches
- bringup: true,
- ),
- ],
- ),
- totConfig: totCIYaml,
- );
- final List<Target> initialTargets = releaseYaml.postsubmitTargets;
- final List<String> initialTargetNames = initialTargets.map((Target target) => target.value.name).toList();
- expect(initialTargetNames, isEmpty);
- });
-
- test('release_build targets for main are not filtered', () {
- final CiYaml releaseYaml = CiYaml(
- slug: Config.flutterSlug,
- branch: 'main',
- config: pb.SchedulerConfig(
- targets: <pb.Target>[
- pb.Target(
- name: 'Linux A',
- properties: <String, String>{'release_build': 'true'},
- ),
- pb.Target(
- name: 'Linux B',
- ),
- pb.Target(
- name: 'Mac A', // Should be ignored on release branches
- bringup: true,
- ),
- ],
- ),
- totConfig: totCIYaml,
- );
- final List<Target> initialTargets = releaseYaml.postsubmitTargets;
- final List<String> initialTargetNames = initialTargets.map((Target target) => target.value.name).toList();
- expect(
- initialTargetNames,
- containsAll(
- <String>[
- 'Linux A',
- ],
- ),
- );
- });
-
- test('validates yaml config', () {
- expect(
- () => CiYaml(
- slug: Config.flutterSlug,
- branch: Config.defaultBranch(Config.flutterSlug),
- config: pb.SchedulerConfig(
- enabledBranches: <String>[
- Config.defaultBranch(Config.flutterSlug),
- ],
- targets: <pb.Target>[
- pb.Target(
- name: 'Linux A',
- ),
- pb.Target(
- name: 'Linux B',
- ),
- ],
- ),
- totConfig: totCIYaml,
- validate: true,
- ),
- throwsA(isA<FormatException>()),
- );
- });
- });
- group('Presubmit validation', () {
- final CiYaml totCIYaml = CiYaml(
- slug: Config.flutterSlug,
- branch: Config.defaultBranch(Config.flutterSlug),
- config: pb.SchedulerConfig(
- enabledBranches: <String>[
- Config.defaultBranch(Config.flutterSlug),
- ],
- targets: <pb.Target>[
- pb.Target(
- name: 'Linux A',
- presubmit: false,
- ),
- pb.Target(
- name: 'Mac A', // Should be ignored on release branches
- bringup: true,
- ),
- ],
- ),
- );
- final CiYaml ciYaml = CiYaml(
- slug: Config.flutterSlug,
- branch: 'flutter-2.4-candidate.3',
- config: pb.SchedulerConfig(
- targets: <pb.Target>[
- pb.Target(
- name: 'Linux A',
- presubmit: true,
- ),
- pb.Target(
- name: 'Linux B',
- ),
- pb.Target(
- name: 'Mac A', // Should be ignored on release branches
- bringup: true,
- ),
- ],
- ),
- totConfig: totCIYaml,
- );
-
- test('presubmit true target is scheduled though TOT is with presubmit false', () {
- final List<Target> initialTargets = ciYaml.presubmitTargets;
- final List<String> initialTargetNames = initialTargets.map((Target target) => target.value.name).toList();
- expect(
- initialTargetNames,
- containsAll(
- <String>[
- 'Linux A',
- ],
- ),
- );
- });
- });
- });
-}
-
-/// Wrapper class for table driven design of [CiYaml.enabledBranchesMatchesCurrentBranch].
-class EnabledBranchesRegexTest {
- EnabledBranchesRegexTest(this.name, this.branch, this.enabledBranches, [this.expectation = true]);
-
- final String branch;
- final List<String> enabledBranches;
- final String name;
- final bool expectation;
-}
diff --git a/app_dart/test/model/ci_yaml/target_test.dart b/app_dart/test/model/ci_yaml/target_test.dart
deleted file mode 100644
index d06d57d..0000000
--- a/app_dart/test/model/ci_yaml/target_test.dart
+++ /dev/null
@@ -1,234 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:cocoon_service/src/model/ci_yaml/target.dart';
-import 'package:cocoon_service/src/model/luci/buildbucket.dart';
-import 'package:cocoon_service/src/model/proto/protos.dart' as pb;
-import 'package:cocoon_service/src/service/scheduler/policy.dart';
-import 'package:github/github.dart' as github;
-import 'package:test/test.dart';
-
-import '../../src/utilities/entity_generators.dart';
-
-void main() {
- group('Target', () {
- group('properties', () {
- test('default properties', () {
- final Target target = generateTarget(1);
- expect(target.getProperties(), <String, Object>{
- 'bringup': false,
- 'dependencies': <String>[],
- });
- });
-
- test('properties with ignore_flakiness true', () {
- final Target target = generateTarget(
- 1,
- platform: 'Mac_ios',
- properties: <String, String>{
- 'ignore_flakiness': 'true',
- },
- );
- expect(target.getIgnoreFlakiness(), true);
- });
-
- test('properties with ignore_flakiness not present', () {
- final Target target = generateTarget(
- 1,
- platform: 'Mac_ios',
- platformProperties: <String, String>{
- // This should be overrided by the target specific property
- 'xcode': 'abc',
- },
- );
- expect(target.getIgnoreFlakiness(), false);
- });
-
- test('properties with ignore_flakiness in platform properties', () {
- final Target target = generateTarget(
- 1,
- platform: 'Mac_ios',
- platformProperties: <String, String>{
- // This should be overrided by the target specific property
- 'ignore_flakiness': 'true',
- },
- );
- expect(target.getIgnoreFlakiness(), true);
- });
-
- test('properties with ignore_flakiness overrides platform properties', () {
- final Target target = generateTarget(
- 1,
- platform: 'Mac_ios',
- platformProperties: <String, String>{
- // This should be overrided by the target specific property
- 'ignore_flakiness': 'true',
- },
- properties: <String, String>{
- 'ignore_flakiness': 'false',
- },
- );
- expect(target.getIgnoreFlakiness(), false);
- });
-
- test('properties with \$flutter/osx_sdk overrides platform properties', () {
- final Target target = generateTarget(
- 1,
- platform: 'Mac',
- platformProperties: <String, String>{
- // This should be overrided by the target specific property
- '\$flutter/osx_sdk': '{"sdk_version": "12abc", "runtime_versions": ["ios-11-0", "ios-12-0"]}',
- },
- properties: <String, String>{
- '\$flutter/osx_sdk': '{"sdk_version": "14e222b", "runtime_versions": ["ios-13-0", "ios-15-0"]}',
- },
- );
- expect(target.getProperties(), <String, Object>{
- 'bringup': false,
- 'dependencies': <String>[],
- '\$flutter/osx_sdk': <String, Object>{
- 'runtime_versions': ['ios-13-0', 'ios-15-0'],
- 'sdk_version': '14e222b',
- },
- });
- });
-
- test('tags are parsed from within properties', () {
- final Target target = generateTarget(
- 1,
- platform: 'Linux_build_test',
- platformProperties: <String, String>{
- // This should be overrided by the target specific property
- 'android_sdk': 'abc',
- },
- properties: <String, String>{'xcode': '12abc', 'tags': '["devicelab", "android", "linux"]'},
- );
- expect(target.tags, ['devicelab', 'android', 'linux']);
- });
-
- test('we do not blow up if tags are not present', () {
- final Target target = generateTarget(
- 1,
- platform: 'Linux_build_test',
- platformProperties: <String, String>{
- // This should be overrided by the target specific property
- 'android_sdk': 'abc',
- },
- );
- expect(target.tags, []);
- });
- });
-
- group('dimensions', () {
- test('no dimensions', () {
- final Target target = generateTarget(1);
- expect(target.getDimensions().length, 0);
- });
-
- test('platform dimensions and target dimensions are combined', () {
- final Target target = generateTarget(
- 1,
- platform: 'Mac_ios',
- platformDimensions: <String, String>{
- 'signing_cert': 'none',
- },
- properties: <String, String>{'os': 'abc', 'cpu': 'x64'},
- );
- final List<RequestedDimension> dimensions = target.getDimensions();
- expect(dimensions.length, 3);
- expect(dimensions[0].key, 'signing_cert');
- expect(dimensions[0].value, 'none');
- expect(dimensions[1].key, 'os');
- expect(dimensions[1].value, 'abc');
- expect(dimensions[2].key, 'cpu');
- expect(dimensions[2].value, 'x64');
- });
-
- test('target specific dimensions overrides platform dimensions', () {
- final Target target = generateTarget(
- 1,
- platform: 'Mac_ios',
- platformDimensions: <String, String>{
- 'signing_cert': 'none',
- },
- dimensions: <String, String>{'signing_cert': 'mac'},
- );
- final List<RequestedDimension> dimensions = target.getDimensions();
- expect(dimensions.length, 1);
- expect(dimensions[0].key, 'signing_cert');
- expect(dimensions[0].value, 'mac');
- });
-
- test('target specific dimensions overrides legacy target specific properties', () {
- final Target target = generateTarget(
- 1,
- platform: 'Windows',
- dimensions: <String, String>{'cpu': 'x64'},
- properties: <String, String>{'cpu': 'x32'},
- );
- final List<RequestedDimension> dimensions = target.getDimensions();
- expect(dimensions.length, 1);
- expect(dimensions[0].key, 'cpu');
- expect(dimensions[0].value, 'x64');
- });
-
- test('target specific dimensions overrides legacy platform properties', () {
- final Target target = generateTarget(
- 1,
- platform: 'Windows',
- dimensions: <String, String>{'cpu': 'x64'},
- platformProperties: <String, String>{'cpu': 'x32'},
- );
- final List<RequestedDimension> dimensions = target.getDimensions();
- expect(dimensions.length, 1);
- expect(dimensions[0].key, 'cpu');
- expect(dimensions[0].value, 'x64');
- });
-
- test('properties are evaluated as string', () {
- final Target target = generateTarget(
- 1,
- platform: 'Mac_ios',
- platformDimensions: <String, String>{
- 'signing_cert': 'none',
- },
- properties: <String, String>{'cores': '32'},
- );
- expect(target.getDimensions().length, 2);
- });
- });
-
- group('scheduler policy', () {
- test('devicelab targets use batch policy', () {
- expect(generateTarget(1, platform: 'Linux_android').schedulerPolicy, isA<BatchPolicy>());
- });
-
- test('devicelab samsung targets use batch policy', () {
- expect(generateTarget(1, platform: 'Linux_samsung_a02').schedulerPolicy, isA<BatchPolicy>());
- });
-
- test('mac host only targets use batch policy', () {
- expect(generateTarget(1, platform: 'Mac').schedulerPolicy, isA<BatchPolicy>());
- });
-
- test('non-cocoon scheduler targets return omit policy', () {
- expect(
- generateTarget(1, platform: 'Linux_android', schedulerSystem: pb.SchedulerSystem.luci).schedulerPolicy,
- isA<OmitPolicy>(),
- );
- });
-
- test('vm cocoon targets return batch policy', () {
- expect(generateTarget(1, platform: 'Linux').schedulerPolicy, isA<BatchPolicy>());
- });
-
- test('packages targets use guaranteed policy', () {
- expect(
- generateTarget(1, platform: 'Mac', slug: github.RepositorySlug('flutter', 'packages')).schedulerPolicy,
- isA<GuaranteedPolicy>(),
- );
- });
- });
- });
-}
diff --git a/app_dart/test/model/commit_test.dart b/app_dart/test/model/commit_test.dart
deleted file mode 100644
index 3907e77..0000000
--- a/app_dart/test/model/commit_test.dart
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2023 The Flutter 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 'package:cocoon_service/src/model/appengine/commit.dart';
-import 'package:cocoon_service/src/service/datastore.dart';
-import 'package:gcloud/db.dart';
-import 'package:github/github.dart';
-import 'package:test/test.dart';
-
-import '../src/datastore/fake_config.dart';
-import '../src/datastore/fake_datastore.dart';
-import '../src/utilities/entity_generators.dart';
-
-void main() {
- group('Commit.composeKey', () {
- test('creates valid key', () {
- final FakeDatastoreDB db = FakeDatastoreDB();
- final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
- const String gitBranch = 'main';
- const String sha = 'abc';
- final key = Commit.createKey(db: db, slug: slug, gitBranch: gitBranch, sha: sha);
- expect(key.id, equals('flutter/flutter/main/abc'));
- });
- });
-
- group('Commit.fromDatastore', () {
- late FakeConfig config;
- late Commit expectedCommit;
-
- setUp(() {
- config = FakeConfig();
- expectedCommit = generateCommit(1);
- config.db.values[expectedCommit.key] = expectedCommit;
- });
-
- test('look up by id', () async {
- final Commit commit = await Commit.fromDatastore(
- datastore: DatastoreService(config.db, 5),
- key: expectedCommit.key,
- );
- expect(commit, expectedCommit);
- });
-
- test('look up by id fails if cannot be found', () async {
- final datastore = DatastoreService(config.db, 5);
- expect(
- Commit.fromDatastore(
- datastore: datastore,
- key: Commit.createKey(
- db: datastore.db,
- slug: RepositorySlug('abc', 'test'),
- gitBranch: 'main',
- sha: 'def',
- ),
- ),
- throwsA(isA<KeyNotFoundException>()),
- );
- });
- });
-}
diff --git a/app_dart/test/model/gerrit/commit_test.dart b/app_dart/test/model/gerrit/commit_test.dart
deleted file mode 100644
index 243377c..0000000
--- a/app_dart/test/model/gerrit/commit_test.dart
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2019 The Flutter 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:convert';
-
-import 'package:cocoon_service/src/model/gerrit/commit.dart';
-import 'package:test/test.dart';
-
-void main() {
- group(GerritCommit, () {
- test('fromJson', () {
- const String json = '''{
- "commit": "c80a772eebe7f47d12ad1b21bc48fbd9521519aa",
- "tree": "ee444f607795706641aedbc7c43a578b001aec5e",
- "parents": [
- "3770382108d17154bbacce45dd18e475718cd904"
- ],
- "author": {
- "name": "recipe-roller",
- "email": "flutter-prod-builder@chops-service-accounts.iam.gserviceaccount.com",
- "time": "Wed Jun 07 22:54:06 2023 +0000"
- },
- "committer": {
- "name": "CQ Bot Account",
- "email": "flutter-scoped@luci-project-accounts.iam.gserviceaccount.com",
- "time": "Wed Jun 07 22:54:06 2023 +0000"
- },
- "message": "Roll recipe dependencies (trivial)\\n\\nThis is an automated CL created by the recipe roller."
- }''';
- final GerritCommit commit = GerritCommit.fromJson(jsonDecode(json));
- expect(commit.author, isNotNull);
- expect(commit.author!.name, 'recipe-roller');
- expect(commit.author!.time, DateTime(2023, 06, 07, 22, 54, 6));
- });
- });
-}
diff --git a/app_dart/test/model/github/checks_test_data.dart b/app_dart/test/model/github/checks_test_data.dart
deleted file mode 100644
index 337467e..0000000
--- a/app_dart/test/model/github/checks_test_data.dart
+++ /dev/null
@@ -1,822 +0,0 @@
-// Copyright 2020 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-String checkSuiteString = checkSuiteTemplate('requested');
-
-String checkSuiteTemplate(String action) => '''\
-{
- "action": "$action",
- "check_suite": {
- "id": 694267587,
- "node_id": "MDEwOkNoZWNrU3VpdGU2OTQyNjc1ODc=",
- "head_branch": "update_licenses",
- "head_sha": "dabc07b74c555c9952f7b63e139f2bb83b75250f",
- "status": "queued",
- "conclusion": "success",
- "url": "https://api.github.com/repos/flutter/cocoon/check-suites/694267587",
- "before": "5763f4c2b3b5e529f4b35c655761a7e818eced2e",
- "after": "dabc07b74c555c9952f7b63e139f2bb83b75250f",
- "pull_requests": [
- {
- "url": "https://api.github.com/repos/flutter/cocoon/pulls/758",
- "id": 409012032,
- "number": 758,
- "head": {
- "ref": "update_licenses",
- "sha": "5763f4c2b3b5e529f4b35c655761a7e818eced2e",
- "repo": {
- "id": 212688278,
- "url": "https://api.github.com/repos/flutter/cocoon",
- "name": "cocoon"
- }
- },
- "base": {
- "ref": "main",
- "sha": "cc430b2e8d6448dfbacf5bcbbd6160cd1fe9dc0b",
- "repo": {
- "id": 63260554,
- "url": "https://api.github.com/repos/flutter/cocoon",
- "name": "cocoon",
- "owner": {
- "avatar_url": "",
- "html_url": "",
- "login": "flutter",
- "id": 54371434
- }
- }
- }
- }
- ],
- "app": {
- "id": 64368,
- "slug": "test",
- "node_id": "MDM6QXBwNjQzNjg=",
- "owner": {
- "login": "abc",
- "id": 54371434,
- "node_id": "MDQ6VXNlcjU0MzcxNDM0",
- "avatar_url": "https://avatars3.githubusercontent.com/u/54371434?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/abc",
- "html_url": "https://github.com/abc",
- "followers_url": "https://api.github.com/users/abc/followers",
- "following_url": "https://api.github.com/users/abc/following{/other_user}",
- "gists_url": "https://api.github.com/users/abc/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/abc/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/abc/subscriptions",
- "organizations_url": "https://api.github.com/users/abc/orgs",
- "repos_url": "https://api.github.com/users/abc/repos",
- "events_url": "https://api.github.com/users/abc/events{/privacy}",
- "received_events_url": "https://api.github.com/users/abc/received_events",
- "type": "User",
- "site_admin": false
- },
- "name": "godofredo-test",
- "description": "",
- "external_url": "https://flutter-dashboard.appspot.com",
- "html_url": "https://github.com/apps/test",
- "created_at": "2020-05-10T00:32:46Z",
- "updated_at": "2020-05-10T00:32:46Z",
- "permissions": {
- "checks": "write",
- "contents": "read",
- "metadata": "read"
- },
- "events": [
- "check_run",
- "check_suite",
- "label"
- ]
- },
- "created_at": "2020-05-18T23:04:25Z",
- "updated_at": "2020-05-18T23:04:25Z",
- "latest_check_runs_count": 0,
- "check_runs_url": "https://api.github.com/repos/flutter/cocoon/check-suites/694267587/check-runs",
- "head_commit": {
- "id": "dabc07b74c555c9952f7b63e139f2bb83b75250f",
- "tree_id": "5f8bd91387bbb9b5db90ab63ac9229224d1b6044",
- "message": "Add checks and tests for license folder.",
- "timestamp": "2020-05-18T23:03:38Z",
- "author": {
- "name": "abc",
- "email": "abc@abcd.com"
- },
- "committer": {
- "name": "abc",
- "email": "abc@abcd.com"
- }
- }
- },
- "repository": {
- "id": 212688278,
- "node_id": "MDEwOlJlcG9zaXRvcnkyMTI2ODgyNzg=",
- "name": "cocoon",
- "full_name": "flutter/cocoon",
- "private": false,
- "owner": {
- "login": "abc",
- "id": 54371434,
- "node_id": "MDQ6VXNlcjU0MzcxNDM0",
- "avatar_url": "https://avatars3.githubusercontent.com/u/54371434?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/abc",
- "html_url": "https://github.com/abc",
- "followers_url": "https://api.github.com/users/abc/followers",
- "following_url": "https://api.github.com/users/abc/following{/other_user}",
- "gists_url": "https://api.github.com/users/abc/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/abc/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/abc/subscriptions",
- "organizations_url": "https://api.github.com/users/abc/orgs",
- "repos_url": "https://api.github.com/users/abc/repos",
- "events_url": "https://api.github.com/users/abc/events{/privacy}",
- "received_events_url": "https://api.github.com/users/abc/received_events",
- "type": "User",
- "site_admin": false
- },
- "html_url": "https://github.com/flutter/cocoon",
- "description": "Flutter's build coordinator and aggregator",
- "fork": true,
- "url": "https://api.github.com/repos/flutter/cocoon",
- "forks_url": "https://api.github.com/repos/flutter/cocoon/forks",
- "keys_url": "https://api.github.com/repos/flutter/cocoon/keys{/key_id}",
- "collaborators_url": "https://api.github.com/repos/flutter/cocoon/collaborators{/collaborator}",
- "teams_url": "https://api.github.com/repos/flutter/cocoon/teams",
- "hooks_url": "https://api.github.com/repos/flutter/cocoon/hooks",
- "issue_events_url": "https://api.github.com/repos/flutter/cocoon/issues/events{/number}",
- "events_url": "https://api.github.com/repos/flutter/cocoon/events",
- "assignees_url": "https://api.github.com/repos/flutter/cocoon/assignees{/user}",
- "branches_url": "https://api.github.com/repos/flutter/cocoon/branches{/branch}",
- "tags_url": "https://api.github.com/repos/flutter/cocoon/tags",
- "blobs_url": "https://api.github.com/repos/flutter/cocoon/git/blobs{/sha}",
- "git_tags_url": "https://api.github.com/repos/flutter/cocoon/git/tags{/sha}",
- "git_refs_url": "https://api.github.com/repos/flutter/cocoon/git/refs{/sha}",
- "trees_url": "https://api.github.com/repos/flutter/cocoon/git/trees{/sha}",
- "statuses_url": "https://api.github.com/repos/flutter/cocoon/statuses/{sha}",
- "languages_url": "https://api.github.com/repos/flutter/cocoon/languages",
- "stargazers_url": "https://api.github.com/repos/flutter/cocoon/stargazers",
- "contributors_url": "https://api.github.com/repos/flutter/cocoon/contributors",
- "subscribers_url": "https://api.github.com/repos/flutter/cocoon/subscribers",
- "subscription_url": "https://api.github.com/repos/flutter/cocoon/subscription",
- "commits_url": "https://api.github.com/repos/flutter/cocoon/commits{/sha}",
- "git_commits_url": "https://api.github.com/repos/flutter/cocoon/git/commits{/sha}",
- "comments_url": "https://api.github.com/repos/flutter/cocoon/comments{/number}",
- "issue_comment_url": "https://api.github.com/repos/flutter/cocoon/issues/comments{/number}",
- "contents_url": "https://api.github.com/repos/flutter/cocoon/contents/{+path}",
- "compare_url": "https://api.github.com/repos/flutter/cocoon/compare/{base}...{head}",
- "merges_url": "https://api.github.com/repos/flutter/cocoon/merges",
- "archive_url": "https://api.github.com/repos/flutter/cocoon/{archive_format}{/ref}",
- "downloads_url": "https://api.github.com/repos/flutter/cocoon/downloads",
- "issues_url": "https://api.github.com/repos/flutter/cocoon/issues{/number}",
- "pulls_url": "https://api.github.com/repos/flutter/cocoon/pulls{/number}",
- "milestones_url": "https://api.github.com/repos/flutter/cocoon/milestones{/number}",
- "notifications_url": "https://api.github.com/repos/flutter/cocoon/notifications{?since,all,participating}",
- "labels_url": "https://api.github.com/repos/flutter/cocoon/labels{/name}",
- "releases_url": "https://api.github.com/repos/flutter/cocoon/releases{/id}",
- "deployments_url": "https://api.github.com/repos/flutter/cocoon/deployments",
- "created_at": "2019-10-03T21:57:12Z",
- "updated_at": "2019-10-03T21:57:14Z",
- "pushed_at": "2020-05-18T23:04:24Z",
- "git_url": "git://github.com/flutter/cocoon.git",
- "ssh_url": "git@github.com:flutter/cocoon.git",
- "clone_url": "https://github.com/flutter/cocoon.git",
- "svn_url": "https://github.com/flutter/cocoon",
- "homepage": null,
- "size": 3070,
- "stargazers_count": 0,
- "watchers_count": 0,
- "language": null,
- "has_issues": false,
- "has_projects": true,
- "has_downloads": true,
- "has_wiki": false,
- "has_pages": false,
- "forks_count": 0,
- "mirror_url": null,
- "archived": false,
- "disabled": false,
- "open_issues_count": 1,
- "license": {
- "key": "clause",
- "name": "License",
- "spdx_id": "",
- "url": "",
- "node_id": "MDc6TGljZW5zZTU="
- },
- "forks": 0,
- "open_issues": 1,
- "watchers": 0,
- "default_branch": "main"
- },
- "sender": {
- "login": "abc",
- "id": 54371434,
- "node_id": "MDQ6VXNlcjU0MzcxNDM0",
- "avatar_url": "https://avatars3.githubusercontent.com/u/54371434?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/abc",
- "html_url": "https://github.com/abc",
- "followers_url": "https://api.github.com/users/abc/followers",
- "following_url": "https://api.github.com/users/abc/following{/other_user}",
- "gists_url": "https://api.github.com/users/abc/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/abc/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/abc/subscriptions",
- "organizations_url": "https://api.github.com/users/abc/orgs",
- "repos_url": "https://api.github.com/users/abc/repos",
- "events_url": "https://api.github.com/users/abc/events{/privacy}",
- "received_events_url": "https://api.github.com/users/abc/received_events",
- "type": "User",
- "site_admin": false
- },
- "installation": {
- "id": 8770981,
- "node_id": "MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uODc3MDk4MQ=="
- }
-}
-''';
-
-const String checkRunString = '''
-{
- "action": "rerequested",
- "check_run": {
- "id": 660053389,
- "node_id": "MDg6Q2hlY2tSdW42NjAwNTMzODk=",
- "head_sha": "66d6bd9a3f79a36fe4f5178ccefbc781488a596c",
- "external_id": "",
- "url": "https://api.github.com/repos/flutter/cocoon/check-runs/660053389",
- "html_url": "https://github.com/flutter/cocoon/runs/660053389",
- "details_url": "https://flutter-dashboard.appspot.com",
- "status": "completed",
- "conclusion": "success",
- "started_at": "2020-05-10T02:49:31Z",
- "completed_at": "2020-05-10T03:11:08Z",
- "output": {
- "title": null,
- "summary": null,
- "text": null,
- "annotations_count": 0,
- "annotations_url": "https://api.github.com/repos/flutter/cocoon/check-runs/660053389/annotations"
- },
- "name": "test1",
- "check_suite": {
- "id": 668083231,
- "node_id": "MDEwOkNoZWNrU3VpdGU2NjgwODMyMzE=",
- "head_branch": "independent_agent",
- "head_sha": "66d6bd9a3f79a36fe4f5178ccefbc781488a596c",
- "status": "queued",
- "conclusion": null,
- "url": "https://api.github.com/repos/flutter/cocoon/check-suites/668083231",
- "before": "918f7fdf0337dac0fca0254e1b0e46e79f8e7a37",
- "after": "66d6bd9a3f79a36fe4f5178ccefbc781488a596c",
- "pull_requests": [
- {
- "url": "https://api.github.com/repos/flutter/cocoon/pulls/1",
- "id": 415645312,
- "number": 1,
- "head": {
- "ref": "independent_agent",
- "sha": "66d6bd9a3f79a36fe4f5178ccefbc781488a596c",
- "repo": {
- "id": 212688278,
- "url": "https://api.github.com/repos/flutter/cocoon",
- "name": "cocoon"
- }
- },
- "base": {
- "ref": "main",
- "sha": "96b953d99588ade4a2b5e9c920813f8f3841b7fb",
- "repo": {
- "id": 212688278,
- "url": "https://api.github.com/repos/flutter/cocoon",
- "name": "cocoon",
- "owner": {
- "avatar_url": "",
- "html_url": "",
- "login": "flutter",
- "id": 54371434
- }
- }
- }
- }
- ],
- "app": {
- "id": 64368,
- "slug": "godofredo-test",
- "node_id": "MDM6QXBwNjQzNjg=",
- "owner": {
- "login": "abc",
- "id": 54371434,
- "node_id": "MDQ6VXNlcjU0MzcxNDM0",
- "avatar_url": "https://avatars3.githubusercontent.com/u/54371434?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/abc",
- "html_url": "https://github.com/abc",
- "followers_url": "https://api.github.com/users/abc/followers",
- "following_url": "https://api.github.com/users/abc/following{/other_user}",
- "gists_url": "https://api.github.com/users/abc/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/abc/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/abc/subscriptions",
- "organizations_url": "https://api.github.com/users/abc/orgs",
- "repos_url": "https://api.github.com/users/abc/repos",
- "events_url": "https://api.github.com/users/abc/events{/privacy}",
- "received_events_url": "https://api.github.com/users/abc/received_events",
- "type": "User",
- "site_admin": false
- },
- "name": "godofredo-test",
- "description": "",
- "external_url": "https://flutter-dashboard.appspot.com",
- "html_url": "https://github.com/apps/godofredo-test",
- "created_at": "2020-05-10T00:32:46Z",
- "updated_at": "2020-05-10T00:32:46Z",
- "permissions": {
- "checks": "write",
- "contents": "read",
- "metadata": "read"
- },
- "events": [
- "check_run",
- "check_suite",
- "label"
- ]
- },
- "created_at": "2020-05-10T01:59:58Z",
- "updated_at": "2020-05-10T01:59:58Z"
- },
- "app": {
- "id": 64368,
- "slug": "godofredo-test",
- "node_id": "MDM6QXBwNjQzNjg=",
- "owner": {
- "login": "abc",
- "id": 54371434,
- "node_id": "MDQ6VXNlcjU0MzcxNDM0",
- "avatar_url": "https://avatars3.githubusercontent.com/u/54371434?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/abc",
- "html_url": "https://github.com/abc",
- "followers_url": "https://api.github.com/users/abc/followers",
- "following_url": "https://api.github.com/users/abc/following{/other_user}",
- "gists_url": "https://api.github.com/users/abc/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/abc/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/abc/subscriptions",
- "organizations_url": "https://api.github.com/users/abc/orgs",
- "repos_url": "https://api.github.com/users/abc/repos",
- "events_url": "https://api.github.com/users/abc/events{/privacy}",
- "received_events_url": "https://api.github.com/users/abc/received_events",
- "type": "User",
- "site_admin": false
- },
- "name": "godofredo-test",
- "description": "",
- "external_url": "https://flutter-dashboard.appspot.com",
- "html_url": "https://github.com/apps/godofredo-test",
- "created_at": "2020-05-10T00:32:46Z",
- "updated_at": "2020-05-10T00:32:46Z",
- "permissions": {
- "checks": "write",
- "contents": "read",
- "metadata": "read"
- },
- "events": [
- "check_run",
- "check_suite",
- "label"
- ]
- },
- "pull_requests": [
- {
- "url": "https://api.github.com/repos/flutter/cocoon/pulls/1",
- "id": 415645312,
- "number": 1,
- "head": {
- "ref": "independent_agent",
- "sha": "66d6bd9a3f79a36fe4f5178ccefbc781488a596c",
- "repo": {
- "id": 212688278,
- "url": "https://api.github.com/repos/flutter/cocoon",
- "name": "cocoon"
- }
- },
- "base": {
- "ref": "main",
- "sha": "96b953d99588ade4a2b5e9c920813f8f3841b7fb",
- "repo": {
- "id": 212688278,
- "url": "https://api.github.com/repos/flutter/cocoon",
- "name": "cocoon",
- "owner": {
- "avatar_url": "",
- "html_url": "",
- "login": "flutter",
- "id": 54371434
- }
- }
- }
- }
- ]
- },
- "repository": {
- "id": 212688278,
- "node_id": "MDEwOlJlcG9zaXRvcnkyMTI2ODgyNzg=",
- "name": "cocoon",
- "full_name": "flutter/cocoon",
- "private": false,
- "owner": {
- "login": "abc",
- "id": 54371434,
- "node_id": "MDQ6VXNlcjU0MzcxNDM0",
- "avatar_url": "https://avatars3.githubusercontent.com/u/54371434?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/abc",
- "html_url": "https://github.com/abc",
- "followers_url": "https://api.github.com/users/abc/followers",
- "following_url": "https://api.github.com/users/abc/following{/other_user}",
- "gists_url": "https://api.github.com/users/abc/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/abc/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/abc/subscriptions",
- "organizations_url": "https://api.github.com/users/abc/orgs",
- "repos_url": "https://api.github.com/users/abc/repos",
- "events_url": "https://api.github.com/users/abc/events{/privacy}",
- "received_events_url": "https://api.github.com/users/abc/received_events",
- "type": "User",
- "site_admin": false
- },
- "html_url": "https://github.com/flutter/cocoon",
- "description": "Flutter's build coordinator and aggregator",
- "fork": true,
- "url": "https://api.github.com/repos/flutter/cocoon",
- "forks_url": "https://api.github.com/repos/flutter/cocoon/forks",
- "keys_url": "https://api.github.com/repos/flutter/cocoon/keys{/key_id}",
- "collaborators_url": "https://api.github.com/repos/flutter/cocoon/collaborators{/collaborator}",
- "teams_url": "https://api.github.com/repos/flutter/cocoon/teams",
- "hooks_url": "https://api.github.com/repos/flutter/cocoon/hooks",
- "issue_events_url": "https://api.github.com/repos/flutter/cocoon/issues/events{/number}",
- "events_url": "https://api.github.com/repos/flutter/cocoon/events",
- "assignees_url": "https://api.github.com/repos/flutter/cocoon/assignees{/user}",
- "branches_url": "https://api.github.com/repos/flutter/cocoon/branches{/branch}",
- "tags_url": "https://api.github.com/repos/flutter/cocoon/tags",
- "blobs_url": "https://api.github.com/repos/flutter/cocoon/git/blobs{/sha}",
- "git_tags_url": "https://api.github.com/repos/flutter/cocoon/git/tags{/sha}",
- "git_refs_url": "https://api.github.com/repos/flutter/cocoon/git/refs{/sha}",
- "trees_url": "https://api.github.com/repos/flutter/cocoon/git/trees{/sha}",
- "statuses_url": "https://api.github.com/repos/flutter/cocoon/statuses/{sha}",
- "languages_url": "https://api.github.com/repos/flutter/cocoon/languages",
- "stargazers_url": "https://api.github.com/repos/flutter/cocoon/stargazers",
- "contributors_url": "https://api.github.com/repos/flutter/cocoon/contributors",
- "subscribers_url": "https://api.github.com/repos/flutter/cocoon/subscribers",
- "subscription_url": "https://api.github.com/repos/flutter/cocoon/subscription",
- "commits_url": "https://api.github.com/repos/flutter/cocoon/commits{/sha}",
- "git_commits_url": "https://api.github.com/repos/flutter/cocoon/git/commits{/sha}",
- "comments_url": "https://api.github.com/repos/flutter/cocoon/comments{/number}",
- "issue_comment_url": "https://api.github.com/repos/flutter/cocoon/issues/comments{/number}",
- "contents_url": "https://api.github.com/repos/flutter/cocoon/contents/{+path}",
- "compare_url": "https://api.github.com/repos/flutter/cocoon/compare/{base}...{head}",
- "merges_url": "https://api.github.com/repos/flutter/cocoon/merges",
- "archive_url": "https://api.github.com/repos/flutter/cocoon/{archive_format}{/ref}",
- "downloads_url": "https://api.github.com/repos/flutter/cocoon/downloads",
- "issues_url": "https://api.github.com/repos/flutter/cocoon/issues{/number}",
- "pulls_url": "https://api.github.com/repos/flutter/cocoon/pulls{/number}",
- "milestones_url": "https://api.github.com/repos/flutter/cocoon/milestones{/number}",
- "notifications_url": "https://api.github.com/repos/flutter/cocoon/notifications{?since,all,participating}",
- "labels_url": "https://api.github.com/repos/flutter/cocoon/labels{/name}",
- "releases_url": "https://api.github.com/repos/flutter/cocoon/releases{/id}",
- "deployments_url": "https://api.github.com/repos/flutter/cocoon/deployments",
- "created_at": "2019-10-03T21:57:12Z",
- "updated_at": "2019-10-03T21:57:14Z",
- "pushed_at": "2020-05-09T23:42:23Z",
- "git_url": "git://github.com/flutter/cocoon.git",
- "ssh_url": "git@github.com:flutter/cocoon.git",
- "clone_url": "https://github.com/flutter/cocoon.git",
- "svn_url": "https://github.com/flutter/cocoon",
- "homepage": null,
- "size": 2941,
- "stargazers_count": 0,
- "watchers_count": 0,
- "language": null,
- "has_issues": false,
- "has_projects": true,
- "has_downloads": true,
- "has_wiki": false,
- "has_pages": false,
- "forks_count": 0,
- "mirror_url": null,
- "archived": false,
- "disabled": false,
- "open_issues_count": 1,
- "license": {
- "key": "",
- "name": "",
- "spdx_id": "",
- "url": "",
- "node_id": "MDc6TGljZW5zZTU="
- },
- "forks": 0,
- "open_issues": 1,
- "watchers": 0,
- "default_branch": "main"
- },
- "sender": {
- "login": "abc",
- "id": 54371434,
- "node_id": "MDQ6VXNlcjU0MzcxNDM0",
- "avatar_url": "https://avatars3.githubusercontent.com/u/54371434?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/abc",
- "html_url": "https://github.com/abc",
- "followers_url": "https://api.github.com/users/abc/followers",
- "following_url": "https://api.github.com/users/abc/following{/other_user}",
- "gists_url": "https://api.github.com/users/abc/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/abc/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/abc/subscriptions",
- "organizations_url": "https://api.github.com/users/abc/orgs",
- "repos_url": "https://api.github.com/users/abc/repos",
- "events_url": "https://api.github.com/users/abc/events{/privacy}",
- "received_events_url": "https://api.github.com/users/abc/received_events",
- "type": "User",
- "site_admin": false
- },
- "installation": {
- "id": 8770981,
- "node_id": "MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uODc3MDk4MQ=="
- }
-}
-''';
-
-const String checkRunWithEmptyPullRequests = '''
-{
- "action": "rerequested",
- "check_run": {
- "id": 660053389,
- "node_id": "MDg6Q2hlY2tSdW42NjAwNTMzODk=",
- "head_sha": "66d6bd9a3f79a36fe4f5178ccefbc781488a596c",
- "external_id": "",
- "url": "https://api.github.com/repos/flutter/cocoon/check-runs/660053389",
- "html_url": "https://github.com/flutter/cocoon/runs/660053389",
- "details_url": "https://flutter-dashboard.appspot.com",
- "status": "completed",
- "conclusion": "success",
- "started_at": "2020-05-10T02:49:31Z",
- "completed_at": "2020-05-10T03:11:08Z",
- "output": {
- "title": null,
- "summary": null,
- "text": null,
- "annotations_count": 0,
- "annotations_url": "https://api.github.com/repos/flutter/cocoon/check-runs/660053389/annotations"
- },
- "name": "test1",
- "check_suite": {
- "id": 668083231,
- "node_id": "MDEwOkNoZWNrU3VpdGU2NjgwODMyMzE=",
- "head_branch": "independent_agent",
- "head_sha": "66d6bd9a3f79a36fe4f5178ccefbc781488a596c",
- "status": "queued",
- "conclusion": null,
- "url": "https://api.github.com/repos/flutter/cocoon/check-suites/668083231",
- "before": "918f7fdf0337dac0fca0254e1b0e46e79f8e7a37",
- "after": "66d6bd9a3f79a36fe4f5178ccefbc781488a596c",
- "pull_requests": [
- {
- "url": "https://api.github.com/repos/flutter/cocoon/pulls/1",
- "id": 415645312,
- "number": 1,
- "head": {
- "ref": "independent_agent",
- "sha": "66d6bd9a3f79a36fe4f5178ccefbc781488a596c",
- "repo": {
- "id": 212688278,
- "url": "https://api.github.com/repos/flutter/cocoon",
- "name": "cocoon"
- }
- },
- "base": {
- "ref": "main",
- "sha": "96b953d99588ade4a2b5e9c920813f8f3841b7fb",
- "repo": {
- "id": 212688278,
- "url": "https://api.github.com/repos/flutter/cocoon",
- "name": "cocoon",
- "owner": {
- "avatar_url": "",
- "html_url": "",
- "login": "flutter",
- "id": 54371434
- }
- }
- }
- }
- ],
- "app": {
- "id": 64368,
- "slug": "godofredo-test",
- "node_id": "MDM6QXBwNjQzNjg=",
- "owner": {
- "login": "abc",
- "id": 54371434,
- "node_id": "MDQ6VXNlcjU0MzcxNDM0",
- "avatar_url": "https://avatars3.githubusercontent.com/u/54371434?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/abc",
- "html_url": "https://github.com/abc",
- "followers_url": "https://api.github.com/users/abc/followers",
- "following_url": "https://api.github.com/users/abc/following{/other_user}",
- "gists_url": "https://api.github.com/users/abc/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/abc/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/abc/subscriptions",
- "organizations_url": "https://api.github.com/users/abc/orgs",
- "repos_url": "https://api.github.com/users/abc/repos",
- "events_url": "https://api.github.com/users/abc/events{/privacy}",
- "received_events_url": "https://api.github.com/users/abc/received_events",
- "type": "User",
- "site_admin": false
- },
- "name": "godofredo-test",
- "description": "",
- "external_url": "https://flutter-dashboard.appspot.com",
- "html_url": "https://github.com/apps/godofredo-test",
- "created_at": "2020-05-10T00:32:46Z",
- "updated_at": "2020-05-10T00:32:46Z",
- "permissions": {
- "checks": "write",
- "contents": "read",
- "metadata": "read"
- },
- "events": [
- "check_run",
- "check_suite",
- "label"
- ]
- },
- "created_at": "2020-05-10T01:59:58Z",
- "updated_at": "2020-05-10T01:59:58Z"
- },
- "app": {
- "id": 64368,
- "slug": "godofredo-test",
- "node_id": "MDM6QXBwNjQzNjg=",
- "owner": {
- "login": "abc",
- "id": 54371434,
- "node_id": "MDQ6VXNlcjU0MzcxNDM0",
- "avatar_url": "https://avatars3.githubusercontent.com/u/54371434?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/abc",
- "html_url": "https://github.com/abc",
- "followers_url": "https://api.github.com/users/abc/followers",
- "following_url": "https://api.github.com/users/abc/following{/other_user}",
- "gists_url": "https://api.github.com/users/abc/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/abc/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/abc/subscriptions",
- "organizations_url": "https://api.github.com/users/abc/orgs",
- "repos_url": "https://api.github.com/users/abc/repos",
- "events_url": "https://api.github.com/users/abc/events{/privacy}",
- "received_events_url": "https://api.github.com/users/abc/received_events",
- "type": "User",
- "site_admin": false
- },
- "name": "godofredo-test",
- "description": "",
- "external_url": "https://flutter-dashboard.appspot.com",
- "html_url": "https://github.com/apps/godofredo-test",
- "created_at": "2020-05-10T00:32:46Z",
- "updated_at": "2020-05-10T00:32:46Z",
- "permissions": {
- "checks": "write",
- "contents": "read",
- "metadata": "read"
- },
- "events": [
- "check_run",
- "check_suite",
- "label"
- ]
- },
- "pull_requests": []
- },
- "repository": {
- "id": 212688278,
- "node_id": "MDEwOlJlcG9zaXRvcnkyMTI2ODgyNzg=",
- "name": "cocoon",
- "full_name": "flutter/cocoon",
- "private": false,
- "owner": {
- "login": "abc",
- "id": 54371434,
- "node_id": "MDQ6VXNlcjU0MzcxNDM0",
- "avatar_url": "https://avatars3.githubusercontent.com/u/54371434?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/abc",
- "html_url": "https://github.com/abc",
- "followers_url": "https://api.github.com/users/abc/followers",
- "following_url": "https://api.github.com/users/abc/following{/other_user}",
- "gists_url": "https://api.github.com/users/abc/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/abc/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/abc/subscriptions",
- "organizations_url": "https://api.github.com/users/abc/orgs",
- "repos_url": "https://api.github.com/users/abc/repos",
- "events_url": "https://api.github.com/users/abc/events{/privacy}",
- "received_events_url": "https://api.github.com/users/abc/received_events",
- "type": "User",
- "site_admin": false
- },
- "html_url": "https://github.com/flutter/cocoon",
- "description": "Flutter's build coordinator and aggregator",
- "fork": true,
- "url": "https://api.github.com/repos/flutter/cocoon",
- "forks_url": "https://api.github.com/repos/flutter/cocoon/forks",
- "keys_url": "https://api.github.com/repos/flutter/cocoon/keys{/key_id}",
- "collaborators_url": "https://api.github.com/repos/flutter/cocoon/collaborators{/collaborator}",
- "teams_url": "https://api.github.com/repos/flutter/cocoon/teams",
- "hooks_url": "https://api.github.com/repos/flutter/cocoon/hooks",
- "issue_events_url": "https://api.github.com/repos/flutter/cocoon/issues/events{/number}",
- "events_url": "https://api.github.com/repos/flutter/cocoon/events",
- "assignees_url": "https://api.github.com/repos/flutter/cocoon/assignees{/user}",
- "branches_url": "https://api.github.com/repos/flutter/cocoon/branches{/branch}",
- "tags_url": "https://api.github.com/repos/flutter/cocoon/tags",
- "blobs_url": "https://api.github.com/repos/flutter/cocoon/git/blobs{/sha}",
- "git_tags_url": "https://api.github.com/repos/flutter/cocoon/git/tags{/sha}",
- "git_refs_url": "https://api.github.com/repos/flutter/cocoon/git/refs{/sha}",
- "trees_url": "https://api.github.com/repos/flutter/cocoon/git/trees{/sha}",
- "statuses_url": "https://api.github.com/repos/flutter/cocoon/statuses/{sha}",
- "languages_url": "https://api.github.com/repos/flutter/cocoon/languages",
- "stargazers_url": "https://api.github.com/repos/flutter/cocoon/stargazers",
- "contributors_url": "https://api.github.com/repos/flutter/cocoon/contributors",
- "subscribers_url": "https://api.github.com/repos/flutter/cocoon/subscribers",
- "subscription_url": "https://api.github.com/repos/flutter/cocoon/subscription",
- "commits_url": "https://api.github.com/repos/flutter/cocoon/commits{/sha}",
- "git_commits_url": "https://api.github.com/repos/flutter/cocoon/git/commits{/sha}",
- "comments_url": "https://api.github.com/repos/flutter/cocoon/comments{/number}",
- "issue_comment_url": "https://api.github.com/repos/flutter/cocoon/issues/comments{/number}",
- "contents_url": "https://api.github.com/repos/flutter/cocoon/contents/{+path}",
- "compare_url": "https://api.github.com/repos/flutter/cocoon/compare/{base}...{head}",
- "merges_url": "https://api.github.com/repos/flutter/cocoon/merges",
- "archive_url": "https://api.github.com/repos/flutter/cocoon/{archive_format}{/ref}",
- "downloads_url": "https://api.github.com/repos/flutter/cocoon/downloads",
- "issues_url": "https://api.github.com/repos/flutter/cocoon/issues{/number}",
- "pulls_url": "https://api.github.com/repos/flutter/cocoon/pulls{/number}",
- "milestones_url": "https://api.github.com/repos/flutter/cocoon/milestones{/number}",
- "notifications_url": "https://api.github.com/repos/flutter/cocoon/notifications{?since,all,participating}",
- "labels_url": "https://api.github.com/repos/flutter/cocoon/labels{/name}",
- "releases_url": "https://api.github.com/repos/flutter/cocoon/releases{/id}",
- "deployments_url": "https://api.github.com/repos/flutter/cocoon/deployments",
- "created_at": "2019-10-03T21:57:12Z",
- "updated_at": "2019-10-03T21:57:14Z",
- "pushed_at": "2020-05-09T23:42:23Z",
- "git_url": "git://github.com/flutter/cocoon.git",
- "ssh_url": "git@github.com:flutter/cocoon.git",
- "clone_url": "https://github.com/flutter/cocoon.git",
- "svn_url": "https://github.com/flutter/cocoon",
- "homepage": null,
- "size": 2941,
- "stargazers_count": 0,
- "watchers_count": 0,
- "language": null,
- "has_issues": false,
- "has_projects": true,
- "has_downloads": true,
- "has_wiki": false,
- "has_pages": false,
- "forks_count": 0,
- "mirror_url": null,
- "archived": false,
- "disabled": false,
- "open_issues_count": 1,
- "license": {
- "key": "",
- "name": "",
- "spdx_id": "",
- "url": "",
- "node_id": "MDc6TGljZW5zZTU="
- },
- "forks": 0,
- "open_issues": 1,
- "watchers": 0,
- "default_branch": "main"
- },
- "sender": {
- "login": "abc",
- "id": 54371434,
- "node_id": "MDQ6VXNlcjU0MzcxNDM0",
- "avatar_url": "https://avatars3.githubusercontent.com/u/54371434?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/abc",
- "html_url": "https://github.com/abc",
- "followers_url": "https://api.github.com/users/abc/followers",
- "following_url": "https://api.github.com/users/abc/following{/other_user}",
- "gists_url": "https://api.github.com/users/abc/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/abc/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/abc/subscriptions",
- "organizations_url": "https://api.github.com/users/abc/orgs",
- "repos_url": "https://api.github.com/users/abc/repos",
- "events_url": "https://api.github.com/users/abc/events{/privacy}",
- "received_events_url": "https://api.github.com/users/abc/received_events",
- "type": "User",
- "site_admin": false
- },
- "installation": {
- "id": 8770981,
- "node_id": "MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uODc3MDk4MQ=="
- }
-}
-''';
diff --git a/app_dart/test/model/google/token_info_test.dart b/app_dart/test/model/google/token_info_test.dart
deleted file mode 100644
index 66995e3..0000000
--- a/app_dart/test/model/google/token_info_test.dart
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:cocoon_service/src/model/google/token_info.dart';
-import 'package:test/test.dart';
-
-void main() {
- group('TokenInfo', () {
- test('deserialize', () {
- final TokenInfo token = TokenInfo.fromJson(<String, dynamic>{
- 'iss': 'issuer',
- 'azp': 'authorizedParty',
- 'aud': 'audience',
- 'sub': 'subject',
- 'hd': 'hostedDomain',
- 'email': 'test@flutter.dev',
- 'email_verified': 'true',
- 'at_hash': 'accessTokenHash',
- 'name': 'Test Flutter',
- 'picture': 'http://example.org/123.jpg',
- 'given_name': 'Test',
- 'family_name': 'Flutter',
- 'locale': 'en',
- 'iat': '12345',
- 'exp': '67890',
- 'jti': 'jwtId',
- 'alg': 'RSA',
- 'kid': 'keyId',
- 'typ': 'JWT',
- });
- expect(token.issuer, 'issuer');
- expect(token.authorizedParty, 'authorizedParty');
- expect(token.audience, 'audience');
- expect(token.subject, 'subject');
- expect(token.hostedDomain, 'hostedDomain');
- expect(token.email, 'test@flutter.dev');
- expect(token.emailIsVerified, isTrue);
- expect(token.accessTokenHash, 'accessTokenHash');
- expect(token.fullName, 'Test Flutter');
- expect(token.profilePictureUrl, 'http://example.org/123.jpg');
- expect(token.givenName, 'Test');
- expect(token.familyName, 'Flutter');
- expect(token.locale, 'en');
- expect(token.issued, DateTime.fromMillisecondsSinceEpoch(12345 * 1000));
- expect(token.expiration, DateTime.fromMillisecondsSinceEpoch(67890 * 1000));
- expect(token.jwtId, 'jwtId');
- expect(token.algorithm, 'RSA');
- expect(token.keyId, 'keyId');
- expect(token.encoding, 'JWT');
- });
- });
-}
diff --git a/app_dart/test/model/model_test.dart b/app_dart/test/model/model_test.dart
deleted file mode 100644
index 53782b5..0000000
--- a/app_dart/test/model/model_test.dart
+++ /dev/null
@@ -1,132 +0,0 @@
-// Copyright 2019 The Flutter 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:mirrors';
-
-import 'package:cocoon_service/cocoon_service.dart';
-import 'package:gcloud/db.dart';
-import 'package:test/test.dart';
-
-// Statically reference something from the Cocoon library to keep the analyzer
-// happy that we're importing it (which we're doing so the mirrors system sees
-// the library).
-const Type libraryReference = Config;
-
-bool isKind(InstanceMirror annotation) => annotation.reflectee.runtimeType == Kind;
-bool isProperty(InstanceMirror annotation) => SymbolName(annotation.type.simpleName).toString().endsWith('Property');
-
-const Map<Symbol, Symbol> propertyAnnotationsTypeToFieldType = <Symbol, Symbol>{
- #BlobProperty: #List,
- #BoolProperty: #bool,
- #DateTimeProperty: #Date,
- #DoubleProperty: #double,
- #IntProperty: #int,
- #ListProperty: #List,
- #ModelKeyProperty: #Key,
- #StringListProperty: #List,
- #StringProperty: #String,
-};
-
-void main() {
- final Iterable<LibraryMirror> libraries = currentMirrorSystem()
- .libraries
- .entries
- .where((MapEntry<Uri, LibraryMirror> entry) => entry.key.path.contains('cocoon_service'))
- .map((MapEntry<Uri, LibraryMirror> entry) => entry.value);
- for (LibraryMirror library in libraries) {
- final Iterable<ClassMirror> classes = library.declarations.values
- .whereType<ClassMirror>()
- .where((ClassMirror declaration) => declaration.hasReflectedType)
- .where((ClassMirror declaration) => declaration.metadata.any(isKind));
- for (ClassMirror modelClass in classes) {
- group('${modelClass.reflectedType}', () {
- test('extends Model', () {
- final bool isStringModel = modelClass.superclass!.reflectedType.toString() == 'Model<String>';
- final bool isIntModel = modelClass.superclass!.reflectedType.toString() == 'Model<int>';
- expect(isStringModel || isIntModel, isTrue);
- });
-
- final Iterable<VariableMirror> propertyVariables = modelClass.declarations.values
- .whereType<VariableMirror>()
- .where((DeclarationMirror declaration) => declaration.metadata.any(isProperty));
-
- for (VariableMirror variable in propertyVariables) {
- final Iterable<InstanceMirror> propertyAnnotations = variable.metadata.where(isProperty);
-
- group(SymbolName(variable.simpleName), () {
- test('contains only one property annotation', () {
- expect(propertyAnnotations, hasLength(1));
- });
-
- test('type matches property annotation type', () {
- final Symbol propertyType = variable.type.simpleName;
- expect(propertyAnnotationsTypeToFieldType[propertyAnnotations.single.type.simpleName], propertyType);
- });
-
- test('is not static', () {
- expect(variable.isStatic, isFalse);
- });
-
- test('is not final', () {
- expect(variable.isFinal, isFalse);
- });
- });
- }
-
- final Iterable<MethodMirror> propertyGetters = modelClass.declarations.values
- .whereType<MethodMirror>()
- .where((MethodMirror method) => method.isGetter)
- .where((DeclarationMirror declaration) => declaration.metadata.any(isProperty));
-
- for (MethodMirror getter in propertyGetters) {
- final Iterable<InstanceMirror> propertyAnnotations = getter.metadata.where(isProperty);
-
- group(SymbolName(getter.simpleName), () {
- test('contains only one property annotation', () {
- expect(propertyAnnotations, hasLength(1));
- });
-
- test('type matches property annotation type', () {
- final Symbol propertyType = getter.returnType.simpleName;
- expect(propertyAnnotationsTypeToFieldType[propertyAnnotations.single.type.simpleName], propertyType);
- });
-
- test('is not static', () {
- expect(getter.isStatic, isFalse);
- });
-
- test('has corresponding setter', () {
- final Iterable<MethodMirror> setter = modelClass.declarations.values
- .whereType<MethodMirror>()
- .where((MethodMirror method) => method.isSetter)
- .where((MethodMirror setter) => setter.simpleName == SymbolName(getter.simpleName).asSetter);
- expect(setter, hasLength(1));
- });
- });
- }
- });
- }
- }
-
- test('SymbolName toString', () {
- expect(const SymbolName(#Foo).toString(), 'Foo');
- });
-}
-
-class SymbolName {
- const SymbolName(this.symbol);
-
- final Symbol symbol;
-
- static final RegExp symbolToString = RegExp(r'^Symbol\("(.*)"\)$');
-
- Symbol get asSetter => Symbol('$this=');
-
- @override
- String toString() {
- final String raw = symbol.toString();
- final RegExpMatch? match = symbolToString.firstMatch(raw);
- return match == null ? raw : match.group(1)!;
- }
-}
diff --git a/app_dart/test/model/push_message_test.dart b/app_dart/test/model/push_message_test.dart
deleted file mode 100644
index 7a16a0d..0000000
--- a/app_dart/test/model/push_message_test.dart
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 2019 The Flutter 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:convert';
-
-import 'package:cocoon_service/src/model/luci/push_message.dart';
-
-import 'package:test/test.dart';
-
-void main() {
- test('BuildPushMessage.fromJson', () {
- final BuildPushMessage data = BuildPushMessage.fromJson(
- json.decode(buildPushMessageJson) as Map<String, dynamic>,
- );
-
- // TODO(chillers): This fails in EMEA timezones. Prefer using UTC instead of local.
- expect(data.build!.createdTimestamp!.year, 2019);
- expect(data.build!.createdTimestamp!.month, 8);
- expect(data.build!.createdTimestamp!.day, 5);
-
- expect(data.build!.tags!.length, 11);
- expect(data.build!.tagsByName('builder').single, 'Linux Coverage');
- expect(data.build!.tagsByName('buildset'), const <String>[
- 'pr/git/37647',
- 'sha/git/0d78fc94f890a64af140ce0a2671ac5fc636f59b',
- ]);
- });
-}
-
-const String buildPushMessageJson = '''{
- "build": {
- "bucket": "luci.flutter.prod",
- "canary": false,
- "canary_preference": "PROD",
- "created_by": "user:dnfield@google.com",
- "created_ts": "1565049186247524",
- "experimental": true,
- "id": "8905920700440101120",
- "parameters_json": "{\\"builder_name\\": \\"Linux Coverage\\", \\"properties\\": {\\"git_ref\\": \\"refs/pull/37647/head\\", \\"git_url\\": \\"https://github.com/flutter/flutter\\"}}",
- "project": "flutter",
- "result_details_json": "{\\"properties\\": {}, \\"swarming\\": {\\"bot_dimensions\\": {\\"caches\\": [\\"flutter_openjdk_install\\", \\"git\\", \\"goma_v2\\", \\"vpython\\"], \\"cores\\": [\\"8\\"], \\"cpu\\": [\\"x86\\", \\"x86-64\\", \\"x86-64-Broadwell_GCE\\", \\"x86-64-avx2\\"], \\"gce\\": [\\"1\\"], \\"gpu\\": [\\"none\\"], \\"id\\": [\\"luci-flutter-prod-xenial-2-bnrz\\"], \\"image\\": [\\"chrome-xenial-19052201-9cb74617499\\"], \\"inside_docker\\": [\\"0\\"], \\"kvm\\": [\\"1\\"], \\"locale\\": [\\"en_US.UTF-8\\"], \\"machine_type\\": [\\"n1-standard-8\\"], \\"os\\": [\\"Linux\\", \\"Ubuntu\\", \\"Ubuntu-16.04\\"], \\"pool\\": [\\"luci.flutter.prod\\"], \\"python\\": [\\"2.7.12\\"], \\"server_version\\": [\\"4382-5929880\\"], \\"ssd\\": [\\"0\\"], \\"zone\\": [\\"us\\", \\"us-central\\", \\"us-central1\\", \\"us-central1-c\\"]}}}",
- "service_account": "flutter-prod-builder@chops-service-accounts.iam.gserviceaccount.com",
- "started_ts": "1565049193786080",
- "status": "STARTED",
- "status_changed_ts": "1565049194386647",
- "tags": [
- "build_address:luci.flutter.prod/Linux Coverage/1698",
- "builder:Linux Coverage",
- "buildset:pr/git/37647",
- "buildset:sha/git/0d78fc94f890a64af140ce0a2671ac5fc636f59b",
- "swarming_hostname:chromium-swarm.appspot.com",
- "swarming_tag:log_location:logdog://logs.chromium.org/flutter/buildbucket/cr-buildbucket.appspot.com/8905920700440101120/+/annotations",
- "swarming_tag:luci_project:flutter",
- "swarming_tag:os:Linux",
- "swarming_tag:recipe_name:flutter/flutter",
- "swarming_tag:recipe_package:infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",
- "swarming_task_id:467d04f2f022d510"
- ],
- "updated_ts": "1565049194391321",
- "url": "https://ci.chromium.org/b/8905920700440101120",
- "utcnow_ts": "1565049194653640"
- },
- "hostname": "cr-buildbucket.appspot.com",
- "user_data": ""
-}''';
diff --git a/app_dart/test/model/stage_test.dart b/app_dart/test/model/stage_test.dart
deleted file mode 100644
index edd592d..0000000
--- a/app_dart/test/model/stage_test.dart
+++ /dev/null
@@ -1,173 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:cocoon_service/src/model/appengine/stage.dart';
-import 'package:cocoon_service/src/model/appengine/task.dart';
-import 'package:test/test.dart';
-
-import '../src/utilities/entity_generators.dart';
-
-Stage buildStage({
- String name = 'stage',
- List<String> statuses = const <String>[Task.statusNew],
-}) {
- final Iterable<Task> tasks = statuses.map<Task>((String status) => generateTask(1, status: status));
- final StageBuilder builder = StageBuilder()
- ..name = name
- ..commit = generateCommit(1)
- ..tasks.addAll(tasks);
- return builder.build();
-}
-
-void main() {
- group('Stage', () {
- test('ordering', () {
- final List<Stage> stages = <Stage>[
- buildStage(name: 'devicelab'),
- buildStage(name: 'unknown'),
- ];
- stages.sort();
- expect(stages.map<String?>((Stage stage) => stage.name), <String>['devicelab', 'unknown']);
- });
-
- test('isManagedByDeviceLab', () {
- expect(buildStage(name: 'devicelab').isManagedByDeviceLab, isTrue);
- expect(buildStage(name: 'unknown').isManagedByDeviceLab, isFalse);
- });
-
- test('taskStatus', () {
- expect(
- buildStage(
- statuses: <String>[
- Task.statusSucceeded,
- ],
- ).taskStatus,
- Task.statusSucceeded,
- );
- expect(
- buildStage(
- statuses: <String>[
- Task.statusSucceeded,
- Task.statusSucceeded,
- Task.statusFailed,
- ],
- ).taskStatus,
- Task.statusFailed,
- );
- expect(
- buildStage(
- statuses: <String>[
- Task.statusNew,
- Task.statusFailed,
- Task.statusNew,
- ],
- ).taskStatus,
- Task.statusFailed,
- );
- expect(
- buildStage(
- statuses: <String>[
- Task.statusInProgress,
- Task.statusFailed,
- Task.statusInProgress,
- ],
- ).taskStatus,
- Task.statusFailed,
- );
- expect(
- buildStage(
- statuses: <String>[
- Task.statusSucceeded,
- Task.statusFailed,
- Task.statusSucceeded,
- ],
- ).taskStatus,
- Task.statusFailed,
- );
- expect(
- buildStage(
- statuses: <String>[
- Task.statusNew,
- Task.statusFailed,
- Task.statusInProgress,
- Task.statusSucceeded,
- ],
- ).taskStatus,
- Task.statusFailed,
- );
- expect(
- buildStage(
- statuses: <String>[
- Task.statusNew,
- Task.statusInProgress,
- Task.statusNew,
- ],
- ).taskStatus,
- Task.statusInProgress,
- );
- expect(
- buildStage(
- statuses: <String>[
- Task.statusNew,
- Task.statusSucceeded,
- Task.statusNew,
- ],
- ).taskStatus,
- Task.statusInProgress,
- );
- expect(
- buildStage(
- statuses: <String>[
- Task.statusSucceeded,
- Task.statusSucceeded,
- Task.statusInProgress,
- ],
- ).taskStatus,
- Task.statusInProgress,
- );
- expect(
- buildStage(
- statuses: <String>[
- Task.statusNew,
- Task.statusNew,
- ],
- ).taskStatus,
- Task.statusNew,
- );
- expect(
- buildStage(
- statuses: <String>[
- Task.statusInProgress,
- Task.statusInProgress,
- ],
- ).taskStatus,
- Task.statusInProgress,
- );
- expect(
- buildStage(
- statuses: <String>[
- Task.statusSucceeded,
- Task.statusSucceeded,
- ],
- ).taskStatus,
- Task.statusSucceeded,
- );
- });
- });
-
- group('StatusBuilder', () {
- test('validates state of the stage', () {
- expect(() => StageBuilder().build(), throwsStateError);
- expect(() => (StageBuilder()..name = 'name').build(), throwsStateError);
- expect(() => (StageBuilder()..commit = generateCommit(1)).build(), throwsStateError);
- expect(
- () => (StageBuilder()
- ..name = 'name'
- ..commit = generateCommit(1))
- .build(),
- throwsStateError,
- );
- });
- });
-}
diff --git a/app_dart/test/model/task_test.dart b/app_dart/test/model/task_test.dart
deleted file mode 100644
index e31bc67..0000000
--- a/app_dart/test/model/task_test.dart
+++ /dev/null
@@ -1,354 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:cocoon_service/src/model/appengine/commit.dart';
-import 'package:cocoon_service/src/model/appengine/task.dart';
-import 'package:cocoon_service/src/model/luci/buildbucket.dart';
-import 'package:cocoon_service/src/model/luci/push_message.dart' as pm;
-import 'package:cocoon_service/src/request_handling/exceptions.dart';
-import 'package:cocoon_service/src/service/datastore.dart';
-import 'package:gcloud/db.dart';
-import 'package:test/test.dart';
-
-import 'package:cocoon_service/src/model/luci/buildbucket.dart' as bb;
-
-import '../src/datastore/fake_config.dart';
-import '../src/utilities/entity_generators.dart';
-
-void main() {
- group('Task', () {
- test('byAttempts comparator', () {
- final List<Task> tasks = <Task>[
- generateTask(1, attempts: 5),
- generateTask(2, attempts: 9),
- generateTask(3, attempts: 3),
- ];
- tasks.sort(Task.byAttempts);
- expect(tasks.map<int>((Task task) => task.attempts!), <int>[3, 5, 9]);
- });
-
- test('disallows illegal status', () {
- expect(() => generateTask(1, status: 'unknown'), throwsArgumentError);
- expect(() => generateTask(1)..status = 'unknown', throwsArgumentError);
- });
-
- group('updateFromBuild', () {
- test('updates if buildNumber is null', () {
- final DateTime created = DateTime.utc(2022, 1, 11, 1, 1);
- final DateTime started = DateTime.utc(2022, 1, 11, 1, 2);
- final DateTime completed = DateTime.utc(2022, 1, 11, 1, 3);
- final pm.Build build = generatePushMessageBuild(
- 1,
- createdTimestamp: created,
- startedTimestamp: started,
- completedTimestamp: completed,
- );
- final Task task = generateTask(1);
-
- expect(task.status, Task.statusNew);
- expect(task.buildNumberList, isNull);
- expect(task.buildNumber, isNull);
- expect(task.endTimestamp, 0);
- expect(task.createTimestamp, 0);
- expect(task.startTimestamp, 0);
-
- task.updateFromBuild(build);
-
- expect(task.status, Task.statusSucceeded);
- expect(task.buildNumber, 1);
- expect(task.buildNumberList, '1');
- expect(task.createTimestamp, created.millisecondsSinceEpoch);
- expect(task.startTimestamp, started.millisecondsSinceEpoch);
- expect(task.endTimestamp, completed.millisecondsSinceEpoch);
- });
-
- test('defaults timestamps to 0', () {
- final pm.Build build = generatePushMessageBuild(1);
- final Task task = generateTask(1);
-
- expect(task.endTimestamp, 0);
- expect(task.createTimestamp, 0);
- expect(task.startTimestamp, 0);
-
- task.updateFromBuild(build);
-
- expect(task.endTimestamp, 0);
- expect(task.createTimestamp, 0);
- expect(task.startTimestamp, 0);
- });
-
- test('updates if buildNumber is prior to pushMessage', () {
- final pm.Build build = generatePushMessageBuild(
- 1,
- buildNumber: 2,
- status: pm.Status.completed,
- result: pm.Result.success,
- );
- final Task task = generateTask(
- 1,
- buildNumber: 1,
- status: Task.statusInProgress,
- );
-
- expect(task.buildNumberList, '1');
- expect(task.status, Task.statusInProgress);
-
- task.updateFromBuild(build);
-
- expect(task.buildNumber, 2);
- expect(task.buildNumberList, '1,2');
- expect(task.status, Task.statusSucceeded);
- });
-
- test('does not duplicate build numbers on multiple messages', () {
- final pm.Build build = generatePushMessageBuild(
- 1,
- status: pm.Status.started,
- );
- final Task task = generateTask(
- 1,
- buildNumber: 1,
- status: Task.statusSucceeded,
- );
-
- expect(task.buildNumber, 1);
- expect(task.buildNumberList, '1');
- expect(task.status, Task.statusSucceeded);
-
- task.updateFromBuild(build);
-
- expect(task.buildNumber, 1);
- expect(task.buildNumberList, '1');
- expect(task.status, Task.statusSucceeded);
- });
-
- test('does not update status if older status', () {
- final pm.Build build = generatePushMessageBuild(
- 1,
- status: pm.Status.started,
- );
- final Task task = generateTask(
- 1,
- buildNumber: 1,
- status: Task.statusSucceeded,
- );
-
- expect(task.buildNumber, 1);
- expect(task.status, Task.statusSucceeded);
-
- task.updateFromBuild(build);
-
- expect(task.buildNumber, 1);
- expect(task.status, Task.statusSucceeded);
- });
-
- test('does not update if build is older than task', () {
- final pm.Build build = generatePushMessageBuild(
- 1,
- status: pm.Status.completed,
- result: pm.Result.success,
- );
- final Task task = generateTask(
- 1,
- buildNumber: 2,
- status: Task.statusNew,
- );
-
- expect(task.buildNumber, 2);
- expect(task.status, Task.statusNew);
-
- task.updateFromBuild(build);
-
- expect(task.buildNumber, 2);
- expect(task.status, Task.statusNew);
- });
-
- test('handles cancelled build', () {
- final pm.Build build = generatePushMessageBuild(
- 1,
- status: pm.Status.completed,
- result: pm.Result.canceled,
- );
- final Task task = generateTask(
- 1,
- buildNumber: 1,
- status: Task.statusNew,
- );
-
- expect(task.status, Task.statusNew);
- task.updateFromBuild(build);
- expect(task.status, Task.statusCancelled);
- });
-
- test('handles infra failed build', () {
- final pm.Build build = generatePushMessageBuild(
- 1,
- status: pm.Status.completed,
- result: pm.Result.failure,
- failureReason: pm.FailureReason.infraFailure,
- );
- final Task task = generateTask(
- 1,
- buildNumber: 1,
- status: Task.statusNew,
- );
-
- expect(task.status, Task.statusNew);
- task.updateFromBuild(build);
- expect(task.status, Task.statusInfraFailure);
- });
- });
- });
-
- group('updateFromBuildbucketBuild', () {
- final DateTime startTime = DateTime(2023, 1, 1, 0, 0, 0);
- final DateTime endTime = DateTime(2023, 1, 1, 0, 14, 23);
- test('updates successfully', () {
- final bb.Build fakeBuild = bb.Build(
- builderId: const BuilderId(project: 'okay-project', bucket: 'good-bucket', builder: 'great-builder'),
- number: 12345,
- id: 'fake-build-id',
- status: bb.Status.success,
- startTime: startTime,
- endTime: endTime,
- input: const Input(
- gitilesCommit: GitilesCommit(
- project: 'flutter/flutter',
- hash: '12341234',
- ref: 'refs/heads/main',
- ),
- ),
- );
-
- final Task task = Task(
- attempts: 1,
- buildNumber: 1234,
- buildNumberList: '1234',
- builderName: 'great-builder',
- commitKey: null,
- createTimestamp: 10,
- endTimestamp: 50,
- luciBucket: 'good-bucket',
- name: 'test123',
- stageName: 'dart-internal',
- startTimestamp: 10,
- status: 'Failed',
- key: null,
- timeoutInMinutes: 0,
- reason: '',
- requiredCapabilities: [],
- reservedForAgentId: '',
- );
-
- task.updateFromBuildbucketBuild(fakeBuild);
-
- final Task expectedUpdatedTask = Task(
- attempts: 2,
- buildNumber: 12345,
- buildNumberList: '1234,12345',
- builderName: 'great-builder',
- commitKey: null,
- createTimestamp: startTime.millisecondsSinceEpoch,
- endTimestamp: endTime.millisecondsSinceEpoch,
- luciBucket: 'good-bucket',
- name: 'test123',
- stageName: 'dart-internal',
- startTimestamp: startTime.millisecondsSinceEpoch,
- status: 'Succeeded',
- key: null,
- timeoutInMinutes: 0,
- reason: '',
- requiredCapabilities: [],
- reservedForAgentId: '',
- );
-
- expect(task.toString(), equals(expectedUpdatedTask.toString()));
- });
- });
-
- // TODO(chillers): There is a bug where `dart test` does not work in offline mode.
- // Need to file issue and get traces.
- group('Task.fromDatastore', () {
- late FakeConfig config;
- late Commit commit;
- late Task expectedTask;
-
- setUp(() {
- config = FakeConfig();
- commit = generateCommit(1);
- expectedTask = generateTask(1, parent: commit);
- config.db.values[commit.key] = commit;
- config.db.values[expectedTask.key] = expectedTask;
- });
-
- test('look up by id', () async {
- final Task task = await Task.fromDatastore(
- datastore: DatastoreService(config.db, 5),
- commitKey: commit.key,
- id: '${expectedTask.id}',
- );
- expect(task, expectedTask);
- });
-
- test('look up by id fails if cannot be found', () async {
- expect(
- Task.fromDatastore(
- datastore: DatastoreService(config.db, 5),
- commitKey: commit.key,
- id: '12345',
- ),
- throwsA(isA<KeyNotFoundException>()),
- );
- });
-
- test('look up by name', () async {
- final Task task = await Task.fromDatastore(
- datastore: DatastoreService(config.db, 5),
- commitKey: commit.key,
- name: expectedTask.name,
- );
- expect(task, expectedTask);
- });
-
- test('look up by name fails if cannot be found', () async {
- try {
- await Task.fromDatastore(
- datastore: DatastoreService(config.db, 5),
- commitKey: commit.key,
- name: 'Linux not_found',
- );
- } catch (e) {
- expect(e, isA<InternalServerError>());
- expect(
- e.toString(),
- equals(
- 'HTTP 500: Expected to find 1 task for Linux not_found, but found 0',
- ),
- );
- }
- });
-
- test('look up by name fails if multiple Tasks with the same name are found', () async {
- final DatastoreService datastore = DatastoreService(config.db, 5);
- final String taskName = expectedTask.name!;
- final Task duplicatedTask = generateTask(2, parent: commit, name: taskName);
- config.db.values[duplicatedTask.key] = duplicatedTask;
- try {
- await Task.fromDatastore(
- datastore: datastore,
- commitKey: commit.key,
- name: taskName,
- );
- } catch (e) {
- expect(e, isA<InternalServerError>());
- expect(
- e.toString(),
- equals(
- 'HTTP 500: Expected to find 1 task for $taskName, but found 2',
- ),
- );
- }
- });
- });
-}
diff --git a/app_dart/test/request_handlers/check_flaky_builders_test.dart b/app_dart/test/request_handlers/check_flaky_builders_test.dart
deleted file mode 100644
index 9c5357f..0000000
--- a/app_dart/test/request_handlers/check_flaky_builders_test.dart
+++ /dev/null
@@ -1,670 +0,0 @@
-// Copyright 2019 The Flutter 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:convert';
-
-import 'package:cocoon_service/ci_yaml.dart';
-import 'package:cocoon_service/cocoon_service.dart';
-import 'package:cocoon_service/src/request_handlers/flaky_handler_utils.dart';
-import 'package:cocoon_service/src/service/bigquery.dart';
-import 'package:cocoon_service/src/service/github_service.dart';
-import 'package:github/github.dart';
-import 'package:mockito/mockito.dart';
-import 'package:test/test.dart';
-import 'package:yaml/yaml.dart';
-import 'package:cocoon_service/src/model/proto/internal/scheduler.pb.dart' as pb;
-
-import '../src/datastore/fake_config.dart';
-import '../src/request_handling/api_request_handler_tester.dart';
-import '../src/request_handling/fake_authentication.dart';
-import '../src/request_handling/fake_http.dart';
-import '../src/utilities/mocks.dart';
-
-import 'check_flaky_builders_test_data.dart';
-
-const String kThreshold = '0.02';
-const String kCurrentMasterSHA = 'b6156fc8d1c6e992fe4ea0b9128f9aef10443bdb';
-const String kCurrentUserName = 'Name';
-const String kCurrentUserLogin = 'login';
-const String kCurrentUserEmail = 'login@email.com';
-
-class MockYaml extends Mock implements CiYaml {}
-
-void main() {
- group('Deflake', () {
- late CheckFlakyBuilders handler;
- late ApiRequestHandlerTester tester;
- FakeHttpRequest request;
- late FakeConfig config;
- FakeClientContext clientContext;
- FakeAuthenticationProvider auth;
- late MockBigqueryService mockBigqueryService;
- MockGitHub mockGitHubClient;
- late MockRepositoriesService mockRepositoriesService;
- late MockPullRequestsService mockPullRequestsService;
- late MockIssuesService mockIssuesService;
- late MockGitService mockGitService;
- MockUsersService mockUsersService;
-
- setUp(() {
- request = FakeHttpRequest(
- queryParametersValue: <String, dynamic>{
- FileFlakyIssueAndPR.kThresholdKey: kThreshold,
- },
- );
-
- clientContext = FakeClientContext();
- auth = FakeAuthenticationProvider(clientContext: clientContext);
- mockBigqueryService = MockBigqueryService();
- mockGitHubClient = MockGitHub();
- mockRepositoriesService = MockRepositoriesService();
- mockIssuesService = MockIssuesService();
- mockPullRequestsService = MockPullRequestsService();
- mockGitService = MockGitService();
- mockUsersService = MockUsersService();
- // when gets the content of .ci.yaml
- when(
- mockRepositoriesService.getContents(
- captureAny,
- kCiYamlPath,
- ),
- ).thenAnswer((Invocation invocation) {
- return Future<RepositoryContents>.value(
- RepositoryContents(file: GitHubFile(content: gitHubEncode(ciYamlContent))),
- );
- });
- // when gets the content of TESTOWNERS
- when(
- mockRepositoriesService.getContents(
- captureAny,
- kTestOwnerPath,
- ),
- ).thenAnswer((Invocation invocation) {
- return Future<RepositoryContents>.value(
- RepositoryContents(file: GitHubFile(content: gitHubEncode(testOwnersContent))),
- );
- });
- // when gets existing marks flaky prs.
- when(mockPullRequestsService.list(captureAny)).thenAnswer((Invocation invocation) {
- return const Stream<PullRequest>.empty();
- });
- // when gets the current head of master branch
- when(mockGitService.getReference(captureAny, kMasterRefs)).thenAnswer((Invocation invocation) {
- return Future<GitReference>.value(
- GitReference(ref: 'refs/$kMasterRefs', object: GitObject('', kCurrentMasterSHA, '')),
- );
- });
- // when gets the current user.
- when(mockUsersService.getCurrentUser()).thenAnswer((Invocation invocation) {
- final CurrentUser result = CurrentUser();
- result.email = kCurrentUserEmail;
- result.name = kCurrentUserName;
- result.login = kCurrentUserLogin;
- return Future<CurrentUser>.value(result);
- });
- // when assigns pull request reviewer.
- when(
- mockGitHubClient.postJSON<Map<String, dynamic>, PullRequest>(
- captureAny,
- statusCode: captureAnyNamed('statusCode'),
- fail: captureAnyNamed('fail'),
- headers: captureAnyNamed('headers'),
- params: captureAnyNamed('params'),
- convert: captureAnyNamed('convert'),
- body: captureAnyNamed('body'),
- preview: captureAnyNamed('preview'),
- ),
- ).thenAnswer((Invocation invocation) {
- return Future<PullRequest>.value(PullRequest());
- });
- when(mockGitHubClient.repositories).thenReturn(mockRepositoriesService);
- when(mockGitHubClient.issues).thenReturn(mockIssuesService);
- when(mockGitHubClient.pullRequests).thenReturn(mockPullRequestsService);
- when(mockGitHubClient.git).thenReturn(mockGitService);
- when(mockGitHubClient.users).thenReturn(mockUsersService);
- config = FakeConfig(
- githubService: GithubService(mockGitHubClient),
- bigqueryService: mockBigqueryService,
- );
- tester = ApiRequestHandlerTester(request: request);
-
- handler = CheckFlakyBuilders(
- config: config,
- authenticationProvider: auth,
- );
- });
-
- test('Can create pr if the flaky test is no longer flaky with a closed issue', () async {
- // When queries flaky data from BigQuery.
- when(
- mockBigqueryService.listRecentBuildRecordsForBuilder(
- kBigQueryProjectId,
- builder: captureAnyNamed('builder'),
- limit: captureAnyNamed('limit'),
- ),
- ).thenAnswer((Invocation invocation) {
- return Future<List<BuilderRecord>>.value(semanticsIntegrationTestRecordsAllPassed);
- });
- // When queries flaky data from BigQuery.
- when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging'))
- .thenAnswer((Invocation invocation) {
- return Future<List<BuilderStatistic>>.value(stagingSemanticsIntegrationTestResponse);
- });
- // When get issue
- when(mockIssuesService.get(captureAny, captureAny)).thenAnswer((_) {
- return Future<Issue>.value(Issue(state: 'closed', htmlUrl: existingIssueURL));
- });
- // When creates git tree
- when(mockGitService.createTree(captureAny, captureAny)).thenAnswer((_) {
- return Future<GitTree>.value(GitTree(expectedSemanticsIntegrationTestTreeSha, '', false, <GitTreeEntry>[]));
- });
- // When creates git commit
- when(mockGitService.createCommit(captureAny, captureAny)).thenAnswer((_) {
- return Future<GitCommit>.value(GitCommit(sha: expectedSemanticsIntegrationTestTreeSha));
- });
- // When creates git reference
- when(mockGitService.createReference(captureAny, captureAny, captureAny)).thenAnswer((Invocation invocation) {
- return Future<GitReference>.value(GitReference(ref: invocation.positionalArguments[1] as String?));
- });
- // When creates pr to deflake test
- when(mockPullRequestsService.create(captureAny, captureAny)).thenAnswer((_) {
- return Future<PullRequest>.value(PullRequest(number: expectedSemanticsIntegrationTestPRNumber));
- });
-
- CheckFlakyBuilders.kRecordNumber = semanticsIntegrationTestRecordsAllPassed.length;
- final Map<String, dynamic> result = await utf8.decoder
- .bind((await tester.get<Body>(handler)).serialize() as Stream<List<int>>)
- .transform(json.decoder)
- .single as Map<String, dynamic>;
-
- // Verify BigQuery is called correctly.
- List<dynamic> captured = verify(
- mockBigqueryService.listRecentBuildRecordsForBuilder(
- captureAny,
- builder: captureAnyNamed('builder'),
- limit: captureAnyNamed('limit'),
- ),
- ).captured;
- expect(captured.length, 3);
- expect(captured[0].toString(), kBigQueryProjectId);
- expect(captured[1] as String?, expectedSemanticsIntegrationTestBuilderName);
- expect(captured[2] as int?, CheckFlakyBuilders.kRecordNumber);
-
- // Verify it gets the correct issue.
- captured = verify(mockIssuesService.get(captureAny, captureAny)).captured;
- expect(captured.length, 2);
- expect(captured[0], Config.flutterSlug);
- expect(captured[1] as int?, existingIssueNumber);
-
- // Verify tree is created correctly.
- captured = verify(mockGitService.createTree(captureAny, captureAny)).captured;
- expect(captured.length, 2);
- expect(captured[0].toString(), '$kCurrentUserLogin/flutter');
- expect(captured[1], isA<CreateGitTree>());
- final CreateGitTree tree = captured[1] as CreateGitTree;
- expect(tree.baseTree, kCurrentMasterSHA);
- expect(tree.entries!.length, 1);
- expect(tree.entries![0].content, expectedSemanticsIntegrationTestCiYamlContent);
- expect(tree.entries![0].path, kCiYamlPath);
- expect(tree.entries![0].mode, kModifyMode);
- expect(tree.entries![0].type, kModifyType);
-
- // Verify commit is created correctly.
- captured = verify(mockGitService.createCommit(captureAny, captureAny)).captured;
- expect(captured.length, 2);
- expect(captured[0].toString(), '$kCurrentUserLogin/flutter');
- expect(captured[1], isA<CreateGitCommit>());
- final CreateGitCommit commit = captured[1] as CreateGitCommit;
- expect(commit.message, expectedSemanticsIntegrationTestPullRequestTitle);
- expect(commit.author!.name, kCurrentUserName);
- expect(commit.author!.email, kCurrentUserEmail);
- expect(commit.committer!.name, kCurrentUserName);
- expect(commit.committer!.email, kCurrentUserEmail);
- expect(commit.tree, expectedSemanticsIntegrationTestTreeSha);
- expect(commit.parents!.length, 1);
- expect(commit.parents![0], kCurrentMasterSHA);
-
- // Verify reference is created correctly.
- captured = verify(mockGitService.createReference(captureAny, captureAny, captureAny)).captured;
- expect(captured.length, 3);
- expect(captured[0].toString(), '$kCurrentUserLogin/flutter');
- expect(captured[2], expectedSemanticsIntegrationTestTreeSha);
- final String? ref = captured[1] as String?;
-
- // Verify pr is created correctly.
- captured = verify(mockPullRequestsService.create(captureAny, captureAny)).captured;
- expect(captured.length, 2);
- expect(captured[0].toString(), Config.flutterSlug.toString());
- expect(captured[1], isA<CreatePullRequest>());
- final CreatePullRequest pr = captured[1] as CreatePullRequest;
- expect(pr.title, expectedSemanticsIntegrationTestPullRequestTitle);
- expect(pr.body, expectedSemanticsIntegrationTestPullRequestBody);
- expect(pr.head, '$kCurrentUserLogin:$ref');
- expect(pr.base, 'refs/$kMasterRefs');
-
- expect(result['Status'], 'success');
- });
-
- test('Can create pr if the flaky test is no longer flaky without an issue', () async {
- // when gets the content of .ci.yaml
- when(
- mockRepositoriesService.getContents(
- captureAny,
- kCiYamlPath,
- ),
- ).thenAnswer((Invocation invocation) {
- return Future<RepositoryContents>.value(
- RepositoryContents(file: GitHubFile(content: gitHubEncode(ciYamlContentNoIssue))),
- );
- });
- // When queries flaky data from BigQuery.
- when(
- mockBigqueryService.listRecentBuildRecordsForBuilder(
- kBigQueryProjectId,
- builder: captureAnyNamed('builder'),
- limit: captureAnyNamed('limit'),
- ),
- ).thenAnswer((Invocation invocation) {
- return Future<List<BuilderRecord>>.value(semanticsIntegrationTestRecordsAllPassed);
- });
- // When queries flaky data from BigQuery.
- when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging'))
- .thenAnswer((Invocation invocation) {
- return Future<List<BuilderStatistic>>.value(stagingSemanticsIntegrationTestResponse);
- });
- // When creates git tree
- when(mockGitService.createTree(captureAny, captureAny)).thenAnswer((_) {
- return Future<GitTree>.value(GitTree(expectedSemanticsIntegrationTestTreeSha, '', false, <GitTreeEntry>[]));
- });
- // When creates git commit
- when(mockGitService.createCommit(captureAny, captureAny)).thenAnswer((_) {
- return Future<GitCommit>.value(GitCommit(sha: expectedSemanticsIntegrationTestTreeSha));
- });
- // When creates git reference
- when(mockGitService.createReference(captureAny, captureAny, captureAny)).thenAnswer((Invocation invocation) {
- return Future<GitReference>.value(GitReference(ref: invocation.positionalArguments[1] as String?));
- });
- // When creates pr to deflake test
- when(mockPullRequestsService.create(captureAny, captureAny)).thenAnswer((_) {
- return Future<PullRequest>.value(PullRequest(number: expectedSemanticsIntegrationTestPRNumber));
- });
-
- CheckFlakyBuilders.kRecordNumber = semanticsIntegrationTestRecordsAllPassed.length;
- final Map<String, dynamic> result = await utf8.decoder
- .bind((await tester.get<Body>(handler)).serialize() as Stream<List<int>>)
- .transform(json.decoder)
- .single as Map<String, dynamic>;
-
- // Verify BigQuery is called correctly.
- List<dynamic> captured = verify(
- mockBigqueryService.listRecentBuildRecordsForBuilder(
- captureAny,
- builder: captureAnyNamed('builder'),
- limit: captureAnyNamed('limit'),
- ),
- ).captured;
- expect(captured.length, 3);
- expect(captured[0].toString(), kBigQueryProjectId);
- expect(captured[1] as String?, expectedSemanticsIntegrationTestBuilderName);
- expect(captured[2] as int?, CheckFlakyBuilders.kRecordNumber);
-
- // Verify it does not get issue.
- verifyNever(mockIssuesService.get(captureAny, captureAny));
-
- // Verify tree is created correctly.
- captured = verify(mockGitService.createTree(captureAny, captureAny)).captured;
- expect(captured.length, 2);
- expect(captured[0].toString(), '$kCurrentUserLogin/flutter');
- expect(captured[1], isA<CreateGitTree>());
- final CreateGitTree tree = captured[1] as CreateGitTree;
- expect(tree.baseTree, kCurrentMasterSHA);
- expect(tree.entries!.length, 1);
- expect(tree.entries![0].content, expectedSemanticsIntegrationTestCiYamlContent);
- expect(tree.entries![0].path, kCiYamlPath);
- expect(tree.entries![0].mode, kModifyMode);
- expect(tree.entries![0].type, kModifyType);
-
- // Verify commit is created correctly.
- captured = verify(mockGitService.createCommit(captureAny, captureAny)).captured;
- expect(captured.length, 2);
- expect(captured[0].toString(), '$kCurrentUserLogin/flutter');
- expect(captured[1], isA<CreateGitCommit>());
- final CreateGitCommit commit = captured[1] as CreateGitCommit;
- expect(commit.message, expectedSemanticsIntegrationTestPullRequestTitle);
- expect(commit.author!.name, kCurrentUserName);
- expect(commit.author!.email, kCurrentUserEmail);
- expect(commit.committer!.name, kCurrentUserName);
- expect(commit.committer!.email, kCurrentUserEmail);
- expect(commit.tree, expectedSemanticsIntegrationTestTreeSha);
- expect(commit.parents!.length, 1);
- expect(commit.parents![0], kCurrentMasterSHA);
-
- // Verify reference is created correctly.
- captured = verify(mockGitService.createReference(captureAny, captureAny, captureAny)).captured;
- expect(captured.length, 3);
- expect(captured[0].toString(), '$kCurrentUserLogin/flutter');
- expect(captured[2], expectedSemanticsIntegrationTestTreeSha);
- final String? ref = captured[1] as String?;
-
- // Verify pr is created correctly.
- captured = verify(mockPullRequestsService.create(captureAny, captureAny)).captured;
- expect(captured.length, 2);
- expect(captured[0].toString(), Config.flutterSlug.toString());
- expect(captured[1], isA<CreatePullRequest>());
- final CreatePullRequest pr = captured[1] as CreatePullRequest;
- expect(pr.title, expectedSemanticsIntegrationTestPullRequestTitle);
- expect(pr.body, expectedSemanticsIntegrationTestPullRequestBodyNoIssue);
- expect(pr.head, '$kCurrentUserLogin:$ref');
- expect(pr.base, 'refs/$kMasterRefs');
-
- expect(result['Status'], 'success');
- });
-
- test('Do not create PR if the builder is in the ignored list', () async {
- // when gets the content of .ci.yaml
- when(
- mockRepositoriesService.getContents(
- captureAny,
- kCiYamlPath,
- ),
- ).thenAnswer((Invocation invocation) {
- return Future<RepositoryContents>.value(
- RepositoryContents(file: GitHubFile(content: gitHubEncode(ciYamlContentFlakyInIgnoreList))),
- );
- });
- // When queries flaky data from BigQuery.
- when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging'))
- .thenAnswer((Invocation invocation) {
- return Future<List<BuilderStatistic>>.value(stagingSemanticsIntegrationTestResponse);
- });
- CheckFlakyBuilders.kRecordNumber = semanticsIntegrationTestRecordsAllPassed.length;
- final Map<String, dynamic> result = await utf8.decoder
- .bind((await tester.get<Body>(handler)).serialize() as Stream<List<int>>)
- .transform(json.decoder)
- .single as Map<String, dynamic>;
-
- // Verify pr is not called correctly.
- verifyNever(mockPullRequestsService.create(captureAny, captureAny)).captured;
-
- expect(result['Status'], 'success');
- });
-
- test('Do not create pr if the issue is still open', () async {
- // When queries flaky data from BigQuery.
- when(
- mockBigqueryService.listRecentBuildRecordsForBuilder(
- kBigQueryProjectId,
- builder: captureAnyNamed('builder'),
- limit: captureAnyNamed('limit'),
- ),
- ).thenAnswer((Invocation invocation) {
- return Future<List<BuilderRecord>>.value(semanticsIntegrationTestRecordsAllPassed);
- });
- // When queries flaky data from BigQuery.
- when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging'))
- .thenAnswer((Invocation invocation) {
- return Future<List<BuilderStatistic>>.value(stagingSemanticsIntegrationTestResponse);
- });
- // When get issue
- when(mockIssuesService.get(captureAny, captureAny)).thenAnswer((_) {
- return Future<Issue>.value(Issue(state: 'open', htmlUrl: existingIssueURL));
- });
- CheckFlakyBuilders.kRecordNumber = semanticsIntegrationTestRecordsAllPassed.length;
- final Map<String, dynamic> result = await utf8.decoder
- .bind((await tester.get<Body>(handler)).serialize() as Stream<List<int>>)
- .transform(json.decoder)
- .single as Map<String, dynamic>;
-
- // Verify it gets the correct issue.
- final List<dynamic> captured = verify(mockIssuesService.get(captureAny, captureAny)).captured;
- expect(captured.length, 2);
- expect(captured[0], Config.flutterSlug);
- expect(captured[1] as int?, existingIssueNumber);
-
- // Verify pr is not created.
- verifyNever(mockPullRequestsService.create(captureAny, captureAny));
-
- expect(result['Status'], 'success');
- });
-
- test('Do not create pr and do not create issue if the records have flaky runs and there is an open issue',
- () async {
- // When queries flaky data from BigQuery.
- when(
- mockBigqueryService.listRecentBuildRecordsForBuilder(
- kBigQueryProjectId,
- builder: captureAnyNamed('builder'),
- limit: captureAnyNamed('limit'),
- ),
- ).thenAnswer((Invocation invocation) {
- return Future<List<BuilderRecord>>.value(semanticsIntegrationTestRecordsFlaky);
- });
- // When get issue
- when(mockIssuesService.get(captureAny, captureAny)).thenAnswer((_) {
- return Future<Issue>.value(
- Issue(
- state: 'closed',
- htmlUrl: existingIssueURL,
- closedAt: DateTime.now().subtract(const Duration(days: kGracePeriodForClosedFlake - 1)),
- ),
- );
- });
- // When queries flaky data from BigQuery.
- when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging'))
- .thenAnswer((Invocation invocation) {
- return Future<List<BuilderStatistic>>.value(stagingSemanticsIntegrationTestResponse);
- });
-
- CheckFlakyBuilders.kRecordNumber = semanticsIntegrationTestRecordsAllPassed.length + 1;
- final Map<String, dynamic> result = await utf8.decoder
- .bind((await tester.get<Body>(handler)).serialize() as Stream<List<int>>)
- .transform(json.decoder)
- .single as Map<String, dynamic>;
-
- // Verify pr is not created.
- verifyNever(mockPullRequestsService.create(captureAny, captureAny));
-
- // Verify issue is created correctly.
- verifyNever(mockPullRequestsService.create(captureAny, captureAny));
-
- expect(result['Status'], 'success');
- });
-
- test('Do not create pr and do not create issue if the records have flaky runs and there is a recently closed issue',
- () async {
- // When get issue
- when(mockIssuesService.get(captureAny, captureAny)).thenAnswer((_) {
- return Future<Issue>.value(
- Issue(
- state: 'open',
- htmlUrl: existingIssueURL,
- ),
- );
- });
- // When queries flaky data from BigQuery.
- when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging'))
- .thenAnswer((Invocation invocation) {
- return Future<List<BuilderStatistic>>.value(stagingSemanticsIntegrationTestResponse);
- });
-
- CheckFlakyBuilders.kRecordNumber = semanticsIntegrationTestRecordsAllPassed.length + 1;
- final Map<String, dynamic> result = await utf8.decoder
- .bind((await tester.get<Body>(handler)).serialize() as Stream<List<int>>)
- .transform(json.decoder)
- .single as Map<String, dynamic>;
-
- // Verify pr is not created.
- verifyNever(mockPullRequestsService.create(captureAny, captureAny));
-
- // Verify issue is created correctly.
- verifyNever(mockPullRequestsService.create(captureAny, captureAny));
-
- expect(result['Status'], 'success');
- });
-
- test('Do not create pr if the records have failed runs', () async {
- // When queries flaky data from BigQuery.
- when(
- mockBigqueryService.listRecentBuildRecordsForBuilder(
- kBigQueryProjectId,
- builder: captureAnyNamed('builder'),
- limit: captureAnyNamed('limit'),
- ),
- ).thenAnswer((Invocation invocation) {
- return Future<List<BuilderRecord>>.value(semanticsIntegrationTestRecordsFailed);
- });
- // When queries flaky data from BigQuery.
- when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging'))
- .thenAnswer((Invocation invocation) {
- return Future<List<BuilderStatistic>>.value(stagingSemanticsIntegrationTestResponse);
- });
- // When get issue
- when(mockIssuesService.get(captureAny, captureAny)).thenAnswer((_) {
- return Future<Issue>.value(
- Issue(
- state: 'closed',
- htmlUrl: existingIssueURL,
- closedAt: DateTime.now().subtract(const Duration(days: 50)),
- ),
- );
- });
-
- CheckFlakyBuilders.kRecordNumber = semanticsIntegrationTestRecordsFailed.length;
- final Map<String, dynamic> result = await utf8.decoder
- .bind((await tester.get<Body>(handler)).serialize() as Stream<List<int>>)
- .transform(json.decoder)
- .single as Map<String, dynamic>;
-
- // Verify BigQuery is called correctly.
- List<dynamic> captured = verify(
- mockBigqueryService.listRecentBuildRecordsForBuilder(
- captureAny,
- builder: captureAnyNamed('builder'),
- limit: captureAnyNamed('limit'),
- ),
- ).captured;
- expect(captured.length, 3);
- expect(captured[0].toString(), kBigQueryProjectId);
- expect(captured[1] as String?, expectedSemanticsIntegrationTestBuilderName);
- expect(captured[2] as int?, CheckFlakyBuilders.kRecordNumber);
-
- // Verify it gets the correct issue.
- captured = verify(mockIssuesService.get(captureAny, captureAny)).captured;
- expect(captured.length, 2);
- expect(captured[0], Config.flutterSlug);
- expect(captured[1] as int?, existingIssueNumber);
-
- // Verify pr is not created.
- verifyNever(mockPullRequestsService.create(captureAny, captureAny));
-
- expect(result['Status'], 'success');
- });
-
- test('Do not create pr if there is an open one', () async {
- // When queries flaky data from BigQuery.
- when(
- mockBigqueryService.listRecentBuildRecordsForBuilder(
- kBigQueryProjectId,
- builder: captureAnyNamed('builder'),
- limit: captureAnyNamed('limit'),
- ),
- ).thenAnswer((Invocation invocation) {
- return Future<List<BuilderRecord>>.value(semanticsIntegrationTestRecordsAllPassed);
- });
- // When queries flaky data from BigQuery.
- when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging'))
- .thenAnswer((Invocation invocation) {
- return Future<List<BuilderStatistic>>.value(stagingSemanticsIntegrationTestResponse);
- });
- // when gets existing marks flaky prs.
- when(mockPullRequestsService.list(captureAny)).thenAnswer((Invocation invocation) {
- return Stream<PullRequest>.value(PullRequest(body: expectedSemanticsIntegrationTestPullRequestBody));
- });
- // When get issue
- when(mockIssuesService.get(captureAny, captureAny)).thenAnswer((_) {
- return Future<Issue>.value(Issue(state: 'closed', htmlUrl: existingIssueURL));
- });
-
- CheckFlakyBuilders.kRecordNumber = semanticsIntegrationTestRecordsAllPassed.length;
- final Map<String, dynamic> result = await utf8.decoder
- .bind((await tester.get<Body>(handler)).serialize() as Stream<List<int>>)
- .transform(json.decoder)
- .single as Map<String, dynamic>;
-
- // Verify pr is not created.
- verifyNever(mockPullRequestsService.create(captureAny, captureAny));
-
- expect(result['Status'], 'success');
- });
-
- test('Do not create pr if not enough records', () async {
- // When queries flaky data from BigQuery.
- when(
- mockBigqueryService.listRecentBuildRecordsForBuilder(
- kBigQueryProjectId,
- builder: captureAnyNamed('builder'),
- limit: captureAnyNamed('limit'),
- ),
- ).thenAnswer((Invocation invocation) {
- return Future<List<BuilderRecord>>.value(semanticsIntegrationTestRecordsAllPassed);
- });
- // When queries flaky data from BigQuery.
- when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging'))
- .thenAnswer((Invocation invocation) {
- return Future<List<BuilderStatistic>>.value(stagingSemanticsIntegrationTestResponse);
- });
- // When get issue
- when(mockIssuesService.get(captureAny, captureAny)).thenAnswer((_) {
- return Future<Issue>.value(
- Issue(
- state: 'closed',
- htmlUrl: existingIssueURL,
- closedAt: DateTime.now().subtract(const Duration(days: 50)),
- ),
- );
- });
-
- CheckFlakyBuilders.kRecordNumber = semanticsIntegrationTestRecordsAllPassed.length + 1;
- final Map<String, dynamic> result = await utf8.decoder
- .bind((await tester.get<Body>(handler)).serialize() as Stream<List<int>>)
- .transform(json.decoder)
- .single as Map<String, dynamic>;
-
- // Verify BigQuery is called correctly.
- List<dynamic> captured = verify(
- mockBigqueryService.listRecentBuildRecordsForBuilder(
- captureAny,
- builder: captureAnyNamed('builder'),
- limit: captureAnyNamed('limit'),
- ),
- ).captured;
- expect(captured.length, 3);
- expect(captured[0].toString(), kBigQueryProjectId);
- expect(captured[1] as String?, expectedSemanticsIntegrationTestBuilderName);
- expect(captured[2] as int?, CheckFlakyBuilders.kRecordNumber);
-
- // Verify it gets the correct issue.
- captured = verify(mockIssuesService.get(captureAny, captureAny)).captured;
- expect(captured.length, 2);
- expect(captured[0], Config.flutterSlug);
- expect(captured[1] as int?, existingIssueNumber);
-
- // Verify pr is not created.
- verifyNever(mockPullRequestsService.create(captureAny, captureAny));
-
- expect(result['Status'], 'success');
- });
-
- test('getIgnoreFlakiness handles non-existing builderame', () async {
- final YamlMap? ci = loadYaml(ciYamlContent) as YamlMap?;
- final pb.SchedulerConfig unCheckedSchedulerConfig = pb.SchedulerConfig()..mergeFromProto3Json(ci);
- final CiYaml ciYaml = CiYaml(
- slug: Config.flutterSlug,
- branch: Config.defaultBranch(Config.flutterSlug),
- config: unCheckedSchedulerConfig,
- );
- CheckFlakyBuilders.getIgnoreFlakiness('Non_existing', ciYaml);
- });
- });
-}
diff --git a/app_dart/test/request_handlers/check_flaky_builders_test_data.dart b/app_dart/test/request_handlers/check_flaky_builders_test_data.dart
deleted file mode 100644
index 35cb0a2..0000000
--- a/app_dart/test/request_handlers/check_flaky_builders_test_data.dart
+++ /dev/null
@@ -1,300 +0,0 @@
-// Copyright 2019 The Flutter 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:convert';
-
-import 'package:cocoon_service/src/service/bigquery.dart';
-
-const int existingIssueNumber = 85578;
-const String existingIssueURL = 'https://github.com/flutter/flutter/issues/$existingIssueNumber';
-
-const String ciYamlContent = '''
-# Describes the targets run in continuous integration environment.
-#
-# Flutter infra uses this file to generate a checklist of tasks to be performed
-# for every commit.
-#
-# More information at:
-# * https://github.com/flutter/cocoon/blob/master/scheduler/README.md
-enabled_branches:
- - master
-
-targets:
- - name: Mac_android android_semantics_integration_test
- presubmit: false
- bringup: true # Flaky $existingIssueURL
- scheduler: luci
- properties:
- tags: >
- ["devicelab"]
- task_name: android_semantics_integration_test
- - name: Mac_android ignore_myflakiness
- bringup: true
- presubmit: false
- scheduler: luci
- properties:
- ignore_flakiness: "true"
- tags: >
- ["devicelab"]
- task_name: ignore_myflakiness
- - name: Linux analyze
- scheduler: luci
- properties:
- tags: >
- ["framework","hostonly"]
- - name: Windows framework_tests_misc
- presubmit: false
- scheduler: luci
- properties:
- tags: >
- ["shard"]
-''';
-
-const String ciYamlContentNoIssue = '''
-# Describes the targets run in continuous integration environment.
-#
-# Flutter infra uses this file to generate a checklist of tasks to be performed
-# for every commit.
-#
-# More information at:
-# * https://github.com/flutter/cocoon/blob/master/scheduler/README.md
-enabled_branches:
- - master
-
-targets:
- - name: Mac_android android_semantics_integration_test
- presubmit: false
- bringup: true
- scheduler: luci
- properties:
- tags: >
- ["devicelab"]
- task_name: android_semantics_integration_test
- - name: Mac_android ignore_myflakiness
- bringup: true
- presubmit: false
- scheduler: luci
- properties:
- ignore_flakiness: "true"
- tags: >
- ["devicelab"]
- task_name: ignore_myflakiness
- - name: Linux analyze
- scheduler: luci
- properties:
- tags: >
- ["framework","hostonly"]
- - name: Windows framework_tests_misc
- presubmit: false
- scheduler: luci
- properties:
- tags: >
- ["shard"]
-''';
-
-const String ciYamlContentFlakyInIgnoreList = '''
-# Describes the targets run in continuous integration environment.
-#
-# Flutter infra uses this file to generate a checklist of tasks to be performed
-# for every commit.
-#
-# More information at:
-# * https://github.com/flutter/cocoon/blob/master/scheduler/README.md
-enabled_branches:
- - master
-
-targets:
- - name: Mac_ios32 native_ui_tests_ios
- presubmit: false
- bringup: true
- scheduler: luci
- properties:
- tags: >
- ["devicelab"]
- task_name: native_ui_tests_ios
-''';
-
-const String testOwnersContent = '''
-
-# Below is a list of Flutter team members' GitHub handles who are
-# test owners of this repository.
-#
-# These owners are mainly team leaders and their sub-teams. Please feel
-# free to claim ownership by adding your handle to corresponding tests.
-#
-# This file will be used as a reference when new flaky bugs are filed and
-# the TL will be assigned and the sub-team will be labeled by default
-# for further triage.
-
-## Linux Android DeviceLab tests
-/dev/devicelab/bin/tasks/android_semantics_integration_test.dart @HansMuller @flutter/framework
-
-## Host only framework tests
-# Linux analyze
-/dev/bots/analyze.dart @HansMuller @flutter/framework
-
-## Shards tests
-# framework_tests @HansMuller @flutter/framework
-
-
-/dev/devicelab/bin/tasks/android_semantics_integration_test.dart @HansMuller @flutter/framework
-''';
-
-const String expectedSemanticsIntegrationTestResponseBody = '''
-<!-- meta-tags: To be used by the automation script only, DO NOT MODIFY.
-{
- "name": "Mac_android android_semantics_integration_test"
-}
--->
-
-The post-submit test builder `Mac_android android_semantics_integration_test`, which has been marked `bringup: true`, had 3 flakes over past 10 commits.
-
-One recent flaky example for a same commit: https://ci.chromium.org/ui/p/flutter/builders/staging/Mac_android%20android_semantics_integration_test/103
-Commit: https://github.com/flutter/flutter/commit/abc
-
-Flaky builds:
-https://ci.chromium.org/ui/p/flutter/builders/staging/Mac_android%20android_semantics_integration_test/103
-https://ci.chromium.org/ui/p/flutter/builders/staging/Mac_android%20android_semantics_integration_test/102
-https://ci.chromium.org/ui/p/flutter/builders/staging/Mac_android%20android_semantics_integration_test/101
-
-Recent test runs:
-https://flutter-dashboard.appspot.com/#/build?taskFilter=Mac_android%20android_semantics_integration_test
-
-Please follow https://github.com/flutter/flutter/wiki/Reducing-Test-Flakiness#fixing-flaky-tests to fix the flakiness and enable the test back after validating the fix (internal dashboard to validate: go/flutter_test_flakiness).
-''';
-
-final List<BuilderRecord> semanticsIntegrationTestRecordsAllPassed = <BuilderRecord>[
- BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false),
- BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false),
- BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false),
- BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false),
- BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false),
- BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false),
- BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false),
- BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false),
-];
-
-final List<BuilderRecord> semanticsIntegrationTestRecordsFlaky = <BuilderRecord>[
- BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false),
- BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false),
- BuilderRecord(commit: 'abc', isFlaky: true, isFailed: false),
- BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false),
- BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false),
- BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false),
- BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false),
- BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false),
-];
-
-final List<BuilderRecord> semanticsIntegrationTestRecordsFailed = <BuilderRecord>[
- BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false),
- BuilderRecord(commit: 'abc', isFlaky: false, isFailed: true),
- BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false),
- BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false),
- BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false),
- BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false),
- BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false),
- BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false),
-];
-
-const String expectedSemanticsIntegrationTestOwner = 'HansMuller';
-const List<String> expectedSemanticsIntegrationTestLabels = <String>[
- 'c: flake',
- 'P0',
- 'framework',
-];
-const String expectedSemanticsIntegrationTestTreeSha = 'abcdefg';
-const int expectedSemanticsIntegrationTestPRNumber = 123;
-
-const String expectedSemanticsIntegrationTestBuilderName = 'Mac_android android_semantics_integration_test';
-const String expectedSemanticsIntegrationTestCiYamlContent = '''
-# Describes the targets run in continuous integration environment.
-#
-# Flutter infra uses this file to generate a checklist of tasks to be performed
-# for every commit.
-#
-# More information at:
-# * https://github.com/flutter/cocoon/blob/master/scheduler/README.md
-enabled_branches:
- - master
-
-targets:
- - name: Mac_android android_semantics_integration_test
- presubmit: false
- scheduler: luci
- properties:
- tags: >
- ["devicelab"]
- task_name: android_semantics_integration_test
- - name: Mac_android ignore_myflakiness
- bringup: true
- presubmit: false
- scheduler: luci
- properties:
- ignore_flakiness: "true"
- tags: >
- ["devicelab"]
- task_name: ignore_myflakiness
- - name: Linux analyze
- scheduler: luci
- properties:
- tags: >
- ["framework","hostonly"]
- - name: Windows framework_tests_misc
- presubmit: false
- scheduler: luci
- properties:
- tags: >
- ["shard"]
-''';
-const String expectedSemanticsIntegrationTestPullRequestTitle =
- 'Marks Mac_android android_semantics_integration_test to be unflaky';
-const String expectedSemanticsIntegrationTestPullRequestBody = '''
-<!-- meta-tags: To be used by the automation script only, DO NOT MODIFY.
-{
- "name": "Mac_android android_semantics_integration_test"
-}
--->
-The issue $existingIssueURL has been closed, and the test has been passing for [8 consecutive runs](https://data.corp.google.com/sites/flutter_infra_metrics_datasite/flutter_check_test_flakiness_status_dashboard/?p=BUILDER_NAME:%22Mac_android%20android_semantics_integration_test%22).
-This test can be marked as unflaky.
-''';
-const String expectedSemanticsIntegrationTestPullRequestBodyNoIssue = '''
-<!-- meta-tags: To be used by the automation script only, DO NOT MODIFY.
-{
- "name": "Mac_android android_semantics_integration_test"
-}
--->
-The test has been passing for [8 consecutive runs](https://data.corp.google.com/sites/flutter_infra_metrics_datasite/flutter_check_test_flakiness_status_dashboard/?p=BUILDER_NAME:%22Mac_android%20android_semantics_integration_test%22).
-This test can be marked as unflaky.
-''';
-
-final List<BuilderStatistic> stagingSemanticsIntegrationTestResponse = <BuilderStatistic>[
- BuilderStatistic(
- name: 'Mac_android android_semantics_integration_test',
- flakyRate: 0.5,
- flakyBuilds: <String>['103', '102', '101'],
- succeededBuilds: <String>['203', '202', '201', '200', '199', '198', '197'],
- recentCommit: 'abc',
- flakyBuildOfRecentCommit: '103',
- flakyNumber: 3,
- totalNumber: 10,
- ),
- // This builder is flakey, but it should be
- // ignored because it has ignore_flakiness set.
- BuilderStatistic(
- name: 'Mac_android ignore_myflakiness',
- flakyRate: 0.5,
- flakyBuilds: <String>['103', '102', '101'],
- succeededBuilds: <String>['203', '202', '201', '200', '199', '198', '197'],
- recentCommit: 'abc',
- flakyBuildOfRecentCommit: '103',
- flakyNumber: 3,
- totalNumber: 10,
- ),
-];
-
-String gitHubEncode(String source) {
- final List<int> utf8Characters = utf8.encode(source);
- final String base64encoded = base64Encode(utf8Characters);
- return base64encoded;
-}
diff --git a/app_dart/test/request_handlers/create_branch_test.dart b/app_dart/test/request_handlers/create_branch_test.dart
deleted file mode 100644
index 134a109..0000000
--- a/app_dart/test/request_handlers/create_branch_test.dart
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2021 The Flutter 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 'package:cocoon_service/src/request_handlers/create_branch.dart';
-import 'package:cocoon_service/src/request_handling/request_handler.dart';
-import 'package:cocoon_service/src/service/branch_service.dart';
-import 'package:mockito/mockito.dart';
-import 'package:test/test.dart';
-
-import '../src/datastore/fake_config.dart';
-import '../src/request_handling/fake_authentication.dart';
-import '../src/request_handling/fake_http.dart';
-import '../src/request_handling/request_handler_tester.dart';
-import '../src/utilities/mocks.dart';
-
-void main() {
- group(CreateBranch, () {
- test('runs', () async {
- final RequestHandlerTester tester = RequestHandlerTester();
- tester.request = FakeHttpRequest(
- queryParametersValue: <String, String>{
- CreateBranch.branchParam: 'flutter-3.7-candidate.1',
- CreateBranch.engineShaParam: 'abc123',
- },
- );
- final BranchService branchService = MockBranchService();
- final RequestHandler handler = CreateBranch(
- branchService: branchService,
- config: FakeConfig(),
- authenticationProvider: FakeAuthenticationProvider(),
- );
- await tester.get(handler);
- verify(branchService.branchFlutterRecipes('flutter-3.7-candidate.1', 'abc123')).called(1);
- });
- });
-}
diff --git a/app_dart/test/request_handlers/dart_internal_subscription_test.dart b/app_dart/test/request_handlers/dart_internal_subscription_test.dart
deleted file mode 100644
index fec7726..0000000
--- a/app_dart/test/request_handlers/dart_internal_subscription_test.dart
+++ /dev/null
@@ -1,308 +0,0 @@
-// Copyright 2023 The Flutter 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 'package:cocoon_service/cocoon_service.dart';
-import 'package:cocoon_service/src/model/appengine/commit.dart';
-import 'package:cocoon_service/src/model/appengine/task.dart';
-import 'package:cocoon_service/src/model/luci/buildbucket.dart';
-import 'package:cocoon_service/src/model/luci/push_message.dart' as push;
-import 'package:cocoon_service/src/service/datastore.dart';
-import 'package:gcloud/db.dart';
-import 'package:mockito/mockito.dart';
-import 'package:test/test.dart';
-
-import '../src/datastore/fake_config.dart';
-import '../src/request_handling/fake_authentication.dart';
-import '../src/request_handling/fake_http.dart';
-import '../src/request_handling/subscription_tester.dart';
-import '../src/utilities/entity_generators.dart';
-import '../src/utilities/mocks.dart';
-
-void main() {
- late DartInternalSubscription handler;
- late FakeConfig config;
- late FakeHttpRequest request;
- late MockBuildBucketClient buildBucketClient;
- late SubscriptionTester tester;
- late Commit commit;
- final DateTime startTime = DateTime(2023, 1, 1, 0, 0, 0);
- final DateTime endTime = DateTime(2023, 1, 1, 0, 14, 23);
- const String project = 'dart-internal';
- const String bucket = 'flutter';
- const String builder = 'Linux packaging_release_builder';
- const int buildId = 123456;
- const String fakeHash = 'HASH12345';
- const String fakeBranch = 'test-branch';
- const String fakePubsubMessage = '''
- {
- "build": {
- "id": "$buildId",
- "builder": {
- "project": "$project",
- "bucket": "$bucket",
- "builder": "$builder"
- }
- }
- }
- ''';
-
- setUp(() async {
- config = FakeConfig();
- buildBucketClient = MockBuildBucketClient();
- handler = DartInternalSubscription(
- cache: CacheService(inMemory: true),
- config: config,
- authProvider: FakeAuthenticationProvider(),
- buildBucketClient: buildBucketClient,
- datastoreProvider: (DatastoreDB db) => DatastoreService(config.db, 5),
- );
- request = FakeHttpRequest();
-
- tester = SubscriptionTester(
- request: request,
- );
-
- commit = generateCommit(
- 1,
- sha: fakeHash,
- branch: fakeBranch,
- owner: 'flutter',
- repo: 'flutter',
- timestamp: 0,
- );
-
- final Build fakeBuild = Build(
- builderId: const BuilderId(project: project, bucket: bucket, builder: builder),
- number: buildId,
- id: 'fake-build-id',
- status: Status.success,
- startTime: startTime,
- endTime: endTime,
- input: const Input(
- gitilesCommit: GitilesCommit(
- project: 'flutter/flutter',
- hash: fakeHash,
- ref: 'refs/heads/$fakeBranch',
- ),
- ),
- );
- when(
- buildBucketClient.getBuild(
- any,
- buildBucketUri: 'https://cr-buildbucket.appspot.com/prpc/buildbucket.v2.Builds',
- ),
- ).thenAnswer((_) => Future<Build>.value(fakeBuild));
-
- final List<Commit> datastoreCommit = <Commit>[commit];
- await config.db.commit(inserts: datastoreCommit);
- });
-
- test('creates a new task successfully', () async {
- tester.message = const push.PushMessage(data: fakePubsubMessage);
-
- await tester.post(handler);
-
- verify(
- buildBucketClient.getBuild(any),
- ).called(1);
-
- // This is used for testing to pull the data out of the "datastore" so that
- // we can verify what was saved.
- late Task taskInDb;
- late Commit commitInDb;
- config.db.values.forEach((k, v) {
- if (v is Task && v.buildNumberList == buildId.toString()) {
- taskInDb = v;
- }
- if (v is Commit) {
- commitInDb = v;
- }
- });
-
- // Ensure the task has the correct parent and commit key
- expect(
- commitInDb.id,
- equals(taskInDb.commitKey?.id),
- );
-
- expect(
- commitInDb.id,
- equals(taskInDb.parentKey?.id),
- );
-
- // Ensure the task in the db is exactly what we expect
- final Task expectedTask = Task(
- attempts: 1,
- buildNumber: buildId,
- buildNumberList: buildId.toString(),
- builderName: builder,
- commitKey: commitInDb.key,
- createTimestamp: startTime.millisecondsSinceEpoch,
- endTimestamp: endTime.millisecondsSinceEpoch,
- luciBucket: bucket,
- name: builder,
- stageName: 'dart-internal',
- startTimestamp: startTime.millisecondsSinceEpoch,
- status: 'Succeeded',
- key: commit.key.append(Task),
- timeoutInMinutes: 0,
- reason: '',
- requiredCapabilities: [],
- reservedForAgentId: '',
- );
-
- expect(
- taskInDb.toString(),
- equals(expectedTask.toString()),
- );
- });
-
- test('updates an existing task successfully', () async {
- const int existingTaskId = 123;
- final Task fakeTask = Task(
- attempts: 1,
- buildNumber: existingTaskId,
- buildNumberList: existingTaskId.toString(),
- builderName: builder,
- commitKey: commit.key,
- createTimestamp: startTime.millisecondsSinceEpoch,
- endTimestamp: endTime.millisecondsSinceEpoch,
- luciBucket: bucket,
- name: builder,
- stageName: 'dart-internal',
- startTimestamp: startTime.millisecondsSinceEpoch,
- status: 'Succeeded',
- key: commit.key.append(Task),
- timeoutInMinutes: 0,
- reason: '',
- requiredCapabilities: [],
- reservedForAgentId: '',
- );
- final List<Task> datastoreCommit = <Task>[fakeTask];
- await config.db.commit(inserts: datastoreCommit);
-
- tester.message = const push.PushMessage(data: fakePubsubMessage);
-
- await tester.post(handler);
-
- verify(
- buildBucketClient.getBuild(any),
- ).called(1);
-
- // This is used for testing to pull the data out of the "datastore" so that
- // we can verify what was saved.
- final String expectedBuilderList = '${existingTaskId.toString()},${buildId.toString()}';
- late Task taskInDb;
- late Commit commitInDb;
- config.db.values.forEach((k, v) {
- if (v is Task && v.buildNumberList == expectedBuilderList) {
- taskInDb = v;
- }
- if (v is Commit) {
- commitInDb = v;
- }
- });
-
- // Ensure the task has the correct parent and commit key
- expect(
- commitInDb.id,
- equals(taskInDb.commitKey?.id),
- );
-
- expect(
- commitInDb.id,
- equals(taskInDb.parentKey?.id),
- );
-
- // Ensure the task in the db is exactly what we expect
- final Task expectedTask = Task(
- attempts: 2,
- buildNumber: buildId,
- buildNumberList: expectedBuilderList,
- builderName: builder,
- commitKey: commitInDb.key,
- createTimestamp: startTime.millisecondsSinceEpoch,
- endTimestamp: endTime.millisecondsSinceEpoch,
- luciBucket: bucket,
- name: builder,
- stageName: 'dart-internal',
- startTimestamp: startTime.millisecondsSinceEpoch,
- status: 'Succeeded',
- key: commit.key.append(Task),
- timeoutInMinutes: 0,
- reason: '',
- requiredCapabilities: [],
- reservedForAgentId: '',
- );
-
- expect(
- taskInDb.toString(),
- equals(expectedTask.toString()),
- );
- });
-
- test('ignores null message', () async {
- tester.message = const push.PushMessage(data: null);
- expect(await tester.post(handler), equals(Body.empty));
- });
-
- test('ignores message with empty build data', () async {
- tester.message = const push.PushMessage(data: '{}');
- expect(await tester.post(handler), equals(Body.empty));
- });
-
- test('ignores message not from flutter bucket', () async {
- tester.message = const push.PushMessage(
- data: '''
- {
- "build": {
- "id": "$buildId",
- "builder": {
- "project": "$project",
- "bucket": "dart",
- "builder": "$builder"
- }
- }
- }
- ''',
- );
- expect(await tester.post(handler), equals(Body.empty));
- });
-
- test('ignores message not from dart-internal project', () async {
- tester.message = const push.PushMessage(
- data: '''
- {
- "build": {
- "id": "$buildId",
- "builder": {
- "project": "different-project",
- "bucket": "$bucket",
- "builder": "$builder"
- }
- }
- }
- ''',
- );
- expect(await tester.post(handler), equals(Body.empty));
- });
-
- test('ignores message not from an accepted builder', () async {
- tester.message = const push.PushMessage(
- data: '''
- {
- "build": {
- "id": "$buildId",
- "builder": {
- "project": "different-project",
- "bucket": "$bucket",
- "builder": "different-builder"
- }
- }
- }
- ''',
- );
- expect(await tester.post(handler), equals(Body.empty));
- });
-}
diff --git a/app_dart/test/request_handlers/file_flaky_issue_and_pr_test.dart b/app_dart/test/request_handlers/file_flaky_issue_and_pr_test.dart
deleted file mode 100644
index f5ec497..0000000
--- a/app_dart/test/request_handlers/file_flaky_issue_and_pr_test.dart
+++ /dev/null
@@ -1,694 +0,0 @@
-// Copyright 2019 The Flutter 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:convert';
-
-import 'package:cocoon_service/ci_yaml.dart';
-import 'package:cocoon_service/cocoon_service.dart';
-import 'package:cocoon_service/src/request_handlers/flaky_handler_utils.dart';
-import 'package:cocoon_service/src/service/bigquery.dart';
-import 'package:cocoon_service/src/service/github_service.dart';
-import 'package:collection/collection.dart';
-import 'package:github/github.dart';
-import 'package:mockito/mockito.dart';
-import 'package:test/test.dart';
-import 'package:yaml/yaml.dart';
-import 'package:cocoon_service/src/model/proto/internal/scheduler.pb.dart' as pb;
-
-import '../src/datastore/fake_config.dart';
-import '../src/request_handling/api_request_handler_tester.dart';
-import '../src/request_handling/fake_authentication.dart';
-import '../src/request_handling/fake_http.dart';
-import '../src/utilities/mocks.dart';
-
-import 'file_flaky_issue_and_pr_test_data.dart';
-
-const String kThreshold = '0.02';
-const String kCurrentMasterSHA = 'b6156fc8d1c6e992fe4ea0b9128f9aef10443bdb';
-const String kCurrentUserName = 'Name';
-const String kCurrentUserLogin = 'login';
-const String kCurrentUserEmail = 'login@email.com';
-
-void main() {
- group('Check flaky', () {
- late FileFlakyIssueAndPR handler;
- late ApiRequestHandlerTester tester;
- late FakeHttpRequest request;
- late FakeConfig config;
- late FakeClientContext clientContext;
- late FakeAuthenticationProvider auth;
- late MockBigqueryService mockBigqueryService;
- late MockGitHub mockGitHubClient;
- late MockRepositoriesService mockRepositoriesService;
- late MockPullRequestsService mockPullRequestsService;
- late MockIssuesService mockIssuesService;
- late MockGitService mockGitService;
- late MockUsersService mockUsersService;
-
- setUp(() {
- request = FakeHttpRequest(
- queryParametersValue: <String, dynamic>{
- FileFlakyIssueAndPR.kThresholdKey: kThreshold,
- },
- );
-
- clientContext = FakeClientContext();
- auth = FakeAuthenticationProvider(clientContext: clientContext);
- mockBigqueryService = MockBigqueryService();
- mockGitHubClient = MockGitHub();
- mockRepositoriesService = MockRepositoriesService();
- mockIssuesService = MockIssuesService();
- mockPullRequestsService = MockPullRequestsService();
- mockGitService = MockGitService();
- mockUsersService = MockUsersService();
- // when gets the content of .ci.yaml
- when(
- mockRepositoriesService.getContents(
- captureAny,
- kCiYamlPath,
- ),
- ).thenAnswer((Invocation invocation) {
- return Future<RepositoryContents>.value(
- RepositoryContents(file: GitHubFile(content: gitHubEncode(ciYamlContent))),
- );
- });
- // when gets the content of TESTOWNERS
- when(
- mockRepositoriesService.getContents(
- captureAny,
- kTestOwnerPath,
- ),
- ).thenAnswer((Invocation invocation) {
- return Future<RepositoryContents>.value(
- RepositoryContents(file: GitHubFile(content: gitHubEncode(testOwnersContent))),
- );
- });
- when(mockIssuesService.create(any, any)).thenAnswer((_) async => Issue());
- // when gets existing flaky issues.
- when(mockIssuesService.listByRepo(captureAny, state: captureAnyNamed('state'), labels: captureAnyNamed('labels')))
- .thenAnswer((Invocation invocation) {
- return const Stream<Issue>.empty();
- });
- // when gets existing marks flaky prs.
- when(mockPullRequestsService.list(captureAny)).thenAnswer((Invocation invocation) {
- return const Stream<PullRequest>.empty();
- });
- // when gets the current head of master branch
- when(mockGitService.getReference(captureAny, kMasterRefs)).thenAnswer((Invocation invocation) {
- return Future<GitReference>.value(
- GitReference(ref: 'refs/$kMasterRefs', object: GitObject('', kCurrentMasterSHA, '')),
- );
- });
- // when gets the current user.
- when(mockUsersService.getCurrentUser()).thenAnswer((Invocation invocation) {
- final CurrentUser result = CurrentUser();
- result.email = kCurrentUserEmail;
- result.name = kCurrentUserName;
- result.login = kCurrentUserLogin;
- return Future<CurrentUser>.value(result);
- });
- // when assigns pull request reviewer.
- when(
- mockGitHubClient.postJSON<Map<String, dynamic>, PullRequest>(
- captureAny,
- statusCode: captureAnyNamed('statusCode'),
- fail: captureAnyNamed('fail'),
- headers: captureAnyNamed('headers'),
- params: captureAnyNamed('params'),
- convert: captureAnyNamed('convert'),
- body: captureAnyNamed('body'),
- preview: captureAnyNamed('preview'),
- ),
- ).thenAnswer((Invocation invocation) {
- return Future<PullRequest>.value(PullRequest());
- });
- when(mockGitHubClient.repositories).thenReturn(mockRepositoriesService);
- when(mockGitHubClient.issues).thenReturn(mockIssuesService);
- when(mockGitHubClient.pullRequests).thenReturn(mockPullRequestsService);
- when(mockGitHubClient.git).thenReturn(mockGitService);
- when(mockGitHubClient.users).thenReturn(mockUsersService);
- config = FakeConfig(
- githubService: GithubService(mockGitHubClient),
- bigqueryService: mockBigqueryService,
- githubClient: mockGitHubClient,
- );
- tester = ApiRequestHandlerTester(request: request);
-
- handler = FileFlakyIssueAndPR(
- config: config,
- authenticationProvider: auth,
- );
- });
-
- test('Can file issue and pr for devicelab test', () async {
- // When queries flaky data from BigQuery.
- when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId)).thenAnswer((Invocation invocation) {
- return Future<List<BuilderStatistic>>.value(semanticsIntegrationTestResponse);
- });
- // When creates issue
- when(mockIssuesService.create(captureAny, captureAny)).thenAnswer((_) {
- return Future<Issue>.value(Issue(htmlUrl: expectedSemanticsIntegrationTestNewIssueURL));
- });
- // Add issue labels
- when(mockIssuesService.addLabelsToIssue(captureAny, captureAny, captureAny)).thenAnswer((_) {
- return Future<List<IssueLabel>>.value(<IssueLabel>[]);
- });
- // When creates git tree
- when(mockGitService.createTree(captureAny, captureAny)).thenAnswer((_) {
- return Future<GitTree>.value(GitTree(expectedSemanticsIntegrationTestTreeSha, '', false, <GitTreeEntry>[]));
- });
- // When creates git commit
- when(mockGitService.createCommit(captureAny, captureAny)).thenAnswer((_) {
- return Future<GitCommit>.value(GitCommit(sha: expectedSemanticsIntegrationTestTreeSha));
- });
- // When creates git reference
- when(mockGitService.createReference(captureAny, captureAny, captureAny)).thenAnswer((Invocation invocation) {
- return Future<GitReference>.value(GitReference(ref: invocation.positionalArguments[1] as String?));
- });
- // When creates pr to mark test flaky
- when(mockPullRequestsService.create(captureAny, captureAny)).thenAnswer((_) {
- return Future<PullRequest>.value(PullRequest(number: expectedSemanticsIntegrationTestPRNumber));
- });
- final Map<String, dynamic> result = await utf8.decoder
- .bind((await tester.get<Body>(handler)).serialize() as Stream<List<int>>)
- .transform(json.decoder)
- .single as Map<String, dynamic>;
-
- // Verify issue is created correctly.
- List<dynamic> captured = verify(mockIssuesService.create(captureAny, captureAny)).captured;
- expect(captured.length, 2);
- expect(captured[0].toString(), Config.flutterSlug.toString());
- expect(captured[1], isA<IssueRequest>());
- final IssueRequest issueRequest = captured[1] as IssueRequest;
- expect(issueRequest.title, expectedSemanticsIntegrationTestResponseTitle);
- expect(issueRequest.body, expectedSemanticsIntegrationTestResponseBody);
- expect(issueRequest.assignee, expectedSemanticsIntegrationTestResponseAssignee);
- expect(
- const ListEquality<String>().equals(issueRequest.labels, expectedSemanticsIntegrationTestResponseLabels),
- isTrue,
- );
-
- // Verify issue label is added correctly.
- captured = verify(mockIssuesService.addLabelsToIssue(captureAny, captureAny, captureAny)).captured;
- expect(captured.length, 3);
- expect(captured[2], ['team-framework']);
-
- // Verify tree is created correctly.
- captured = verify(mockGitService.createTree(captureAny, captureAny)).captured;
- expect(captured.length, 2);
- expect(captured[0].toString(), '$kCurrentUserLogin/flutter');
- expect(captured[1], isA<CreateGitTree>());
- final CreateGitTree tree = captured[1] as CreateGitTree;
- expect(tree.baseTree, kCurrentMasterSHA);
- expect(tree.entries!.length, 1);
- expect(tree.entries![0].content, expectedSemanticsIntegrationTestCiYamlContent);
- expect(tree.entries![0].path, kCiYamlPath);
- expect(tree.entries![0].mode, kModifyMode);
- expect(tree.entries![0].type, kModifyType);
-
- // Verify commit is created correctly.
- captured = verify(mockGitService.createCommit(captureAny, captureAny)).captured;
- expect(captured.length, 2);
- expect(captured[0].toString(), '$kCurrentUserLogin/flutter');
- expect(captured[1], isA<CreateGitCommit>());
- final CreateGitCommit commit = captured[1] as CreateGitCommit;
- expect(commit.message, expectedSemanticsIntegrationTestPullRequestTitle);
- expect(commit.author!.name, kCurrentUserName);
- expect(commit.author!.email, kCurrentUserEmail);
- expect(commit.committer!.name, kCurrentUserName);
- expect(commit.committer!.email, kCurrentUserEmail);
- expect(commit.tree, expectedSemanticsIntegrationTestTreeSha);
- expect(commit.parents!.length, 1);
- expect(commit.parents![0], kCurrentMasterSHA);
-
- // Verify reference is created correctly.
- captured = verify(mockGitService.createReference(captureAny, captureAny, captureAny)).captured;
- expect(captured.length, 3);
- expect(captured[0].toString(), '$kCurrentUserLogin/flutter');
- expect(captured[2], expectedSemanticsIntegrationTestTreeSha);
- final String? ref = captured[1] as String?;
-
- // Verify pr is created correctly.
- captured = verify(mockPullRequestsService.create(captureAny, captureAny)).captured;
- expect(captured.length, 2);
- expect(captured[0].toString(), Config.flutterSlug.toString());
- expect(captured[1], isA<CreatePullRequest>());
- final CreatePullRequest pr = captured[1] as CreatePullRequest;
- expect(pr.title, expectedSemanticsIntegrationTestPullRequestTitle);
- expect(pr.body, expectedSemanticsIntegrationTestPullRequestBody);
- expect(pr.head, '$kCurrentUserLogin:$ref');
- expect(pr.base, 'refs/$kMasterRefs');
-
- expect(result['Status'], 'success');
- });
-
- test('File mulitple issues and prs', () async {
- // when gets the content of .ci.yaml
- when(
- mockRepositoriesService.getContents(
- captureAny,
- kCiYamlPath,
- ),
- ).thenAnswer((Invocation invocation) {
- return Future<RepositoryContents>.value(
- RepositoryContents(file: GitHubFile(content: gitHubEncode(ciYamlContentTwoFlakyTargets))),
- );
- });
- // When queries flaky data from BigQuery.
- when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId)).thenAnswer((Invocation invocation) {
- return Future<List<BuilderStatistic>>.value(semanticsIntegrationTestResponse);
- });
- // When creates issue
- when(mockIssuesService.create(captureAny, captureAny)).thenAnswer((_) {
- return Future<Issue>.value(Issue(htmlUrl: expectedSemanticsIntegrationTestNewIssueURL));
- });
- // Add issue labels
- when(mockIssuesService.addLabelsToIssue(captureAny, captureAny, captureAny)).thenAnswer((_) {
- return Future<List<IssueLabel>>.value(<IssueLabel>[]);
- });
- // When creates git tree
- when(mockGitService.createTree(captureAny, captureAny)).thenAnswer((_) {
- return Future<GitTree>.value(GitTree(expectedSemanticsIntegrationTestTreeSha, '', false, <GitTreeEntry>[]));
- });
- // When creates git commit
- when(mockGitService.createCommit(captureAny, captureAny)).thenAnswer((_) {
- return Future<GitCommit>.value(GitCommit(sha: expectedSemanticsIntegrationTestTreeSha));
- });
- // When creates git reference
- when(mockGitService.createReference(captureAny, captureAny, captureAny)).thenAnswer((Invocation invocation) {
- return Future<GitReference>.value(GitReference(ref: invocation.positionalArguments[1] as String?));
- });
- // When creates pr to mark test flaky
- when(mockPullRequestsService.create(captureAny, captureAny)).thenAnswer((_) {
- return Future<PullRequest>.value(PullRequest(number: expectedSemanticsIntegrationTestPRNumber));
- });
- final Map<String, dynamic> result = await utf8.decoder
- .bind((await tester.get<Body>(handler)).serialize() as Stream<List<int>>)
- .transform(json.decoder)
- .single as Map<String, dynamic>;
- expect(result['Status'], 'success');
- expect(result['NumberOfCreatedIssuesAndPRs'], 2);
- });
-
- test('File issues and prs up to issueAndPRLimit', () async {
- // when gets the content of .ci.yaml
- config = FakeConfig(
- githubService: GithubService(mockGitHubClient),
- bigqueryService: mockBigqueryService,
- githubClient: mockGitHubClient,
- issueAndPRLimitValue: 1,
- );
- handler = FileFlakyIssueAndPR(
- config: config,
- authenticationProvider: auth,
- );
- when(
- mockRepositoriesService.getContents(
- captureAny,
- kCiYamlPath,
- ),
- ).thenAnswer((Invocation invocation) {
- return Future<RepositoryContents>.value(
- RepositoryContents(file: GitHubFile(content: gitHubEncode(ciYamlContentTwoFlakyTargets))),
- );
- });
- // When queries flaky data from BigQuery.
- when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId)).thenAnswer((Invocation invocation) {
- return Future<List<BuilderStatistic>>.value(semanticsIntegrationTestResponse);
- });
- // When creates issue
- when(mockIssuesService.create(captureAny, captureAny)).thenAnswer((_) {
- return Future<Issue>.value(Issue(htmlUrl: expectedSemanticsIntegrationTestNewIssueURL));
- });
- // Add issue labels
- when(mockIssuesService.addLabelsToIssue(captureAny, captureAny, captureAny)).thenAnswer((_) {
- return Future<List<IssueLabel>>.value(<IssueLabel>[]);
- });
- // When creates git tree
- when(mockGitService.createTree(captureAny, captureAny)).thenAnswer((_) {
- return Future<GitTree>.value(GitTree(expectedSemanticsIntegrationTestTreeSha, '', false, <GitTreeEntry>[]));
- });
- // When creates git commit
- when(mockGitService.createCommit(captureAny, captureAny)).thenAnswer((_) {
- return Future<GitCommit>.value(GitCommit(sha: expectedSemanticsIntegrationTestTreeSha));
- });
- // When creates git reference
- when(mockGitService.createReference(captureAny, captureAny, captureAny)).thenAnswer((Invocation invocation) {
- return Future<GitReference>.value(GitReference(ref: invocation.positionalArguments[1] as String?));
- });
- // When creates pr to mark test flaky
- when(mockPullRequestsService.create(captureAny, captureAny)).thenAnswer((_) {
- return Future<PullRequest>.value(PullRequest(number: expectedSemanticsIntegrationTestPRNumber));
- });
- final Map<String, dynamic> result = await utf8.decoder
- .bind((await tester.get<Body>(handler)).serialize() as Stream<List<int>>)
- .transform(json.decoder)
- .single as Map<String, dynamic>;
- expect(result['Status'], 'success');
- expect(result['NumberOfCreatedIssuesAndPRs'], 1);
- });
-
- test('Can file issue and pr for framework host-only test', () async {
- // When queries flaky data from BigQuery.
- when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId)).thenAnswer((Invocation invocation) {
- return Future<List<BuilderStatistic>>.value(analyzeTestResponse);
- });
- // When creates issue
- when(mockIssuesService.create(captureAny, captureAny)).thenAnswer((_) {
- return Future<Issue>.value(Issue(htmlUrl: expectedSemanticsIntegrationTestNewIssueURL));
- });
- // Add issue labels
- when(mockIssuesService.addLabelsToIssue(captureAny, captureAny, captureAny)).thenAnswer((_) {
- return Future<List<IssueLabel>>.value(<IssueLabel>[]);
- });
- // When creates git tree
- when(mockGitService.createTree(captureAny, captureAny)).thenAnswer((_) {
- return Future<GitTree>.value(GitTree(expectedSemanticsIntegrationTestTreeSha, '', false, <GitTreeEntry>[]));
- });
- // When creates git commit
- when(mockGitService.createCommit(captureAny, captureAny)).thenAnswer((_) {
- return Future<GitCommit>.value(GitCommit(sha: expectedSemanticsIntegrationTestTreeSha));
- });
- // When creates git reference
- when(mockGitService.createReference(captureAny, captureAny, captureAny)).thenAnswer((Invocation invocation) {
- return Future<GitReference>.value(GitReference(ref: invocation.positionalArguments[1] as String?));
- });
- // When creates pr to mark test flaky
- when(mockPullRequestsService.create(captureAny, captureAny)).thenAnswer((_) {
- return Future<PullRequest>.value(PullRequest(number: expectedSemanticsIntegrationTestPRNumber));
- });
- final Map<String, dynamic> result = await utf8.decoder
- .bind((await tester.get<Body>(handler)).serialize() as Stream<List<int>>)
- .transform(json.decoder)
- .single as Map<String, dynamic>;
-
- // Verify issue is created correctly.
- List<dynamic> captured = verify(mockIssuesService.create(captureAny, captureAny)).captured;
- expect(captured.length, 2);
- expect(captured[0].toString(), Config.flutterSlug.toString());
- expect(captured[1], isA<IssueRequest>());
- final IssueRequest issueRequest = captured[1] as IssueRequest;
- expect(issueRequest.assignee, expectedAnalyzeTestResponseAssignee);
- expect(const ListEquality<String>().equals(issueRequest.labels, expectedAnalyzeTestResponseLabels), isTrue);
-
- // Verify pr is created correctly.
- captured = verify(mockPullRequestsService.create(captureAny, captureAny)).captured;
- expect(captured.length, 2);
- expect(captured[0].toString(), Config.flutterSlug.toString());
- expect(captured[1], isA<CreatePullRequest>());
-
- expect(result['Status'], 'success');
- });
-
- test('Can file issue when limited number of successfuly builds exist', () async {
- // When queries flaky data from BigQuery.
- when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId)).thenAnswer((Invocation invocation) {
- return Future<List<BuilderStatistic>>.value(limitedNumberOfBuildsResponse);
- });
- // When creates issue
- when(mockIssuesService.create(captureAny, captureAny)).thenAnswer((_) {
- return Future<Issue>.value(Issue(htmlUrl: expectedSemanticsIntegrationTestNewIssueURL));
- });
- // Add issue labels
- when(mockIssuesService.addLabelsToIssue(captureAny, captureAny, captureAny)).thenAnswer((_) {
- return Future<List<IssueLabel>>.value(<IssueLabel>[]);
- });
- // When creates git tree
- when(mockGitService.createTree(captureAny, captureAny)).thenAnswer((_) {
- return Future<GitTree>.value(GitTree(expectedSemanticsIntegrationTestTreeSha, '', false, <GitTreeEntry>[]));
- });
- // When creates git commit
- when(mockGitService.createCommit(captureAny, captureAny)).thenAnswer((_) {
- return Future<GitCommit>.value(GitCommit(sha: expectedSemanticsIntegrationTestTreeSha));
- });
- // When creates git reference
- when(mockGitService.createReference(captureAny, captureAny, captureAny)).thenAnswer((Invocation invocation) {
- return Future<GitReference>.value(GitReference(ref: invocation.positionalArguments[1] as String?));
- });
- // When creates pr to mark test flaky
- when(mockPullRequestsService.create(captureAny, captureAny)).thenAnswer((_) {
- return Future<PullRequest>.value(PullRequest(number: expectedSemanticsIntegrationTestPRNumber));
- });
- final Map<String, dynamic> result = await utf8.decoder
- .bind((await tester.get<Body>(handler)).serialize() as Stream<List<int>>)
- .transform(json.decoder)
- .single as Map<String, dynamic>;
-
- // Verify issue is created correctly.
- final List<dynamic> captured = verify(mockIssuesService.create(captureAny, captureAny)).captured;
- expect(captured.length, 2);
- expect(captured[0].toString(), Config.flutterSlug.toString());
- expect(captured[1], isA<IssueRequest>());
- final IssueRequest issueRequest = captured[1] as IssueRequest;
- expect(issueRequest.body, expectedLimitedNumberOfBuildsResponseBody);
-
- expect(result['Status'], 'success');
- });
-
- test('Can file issue but not pr for shard test', () async {
- // When queries flaky data from BigQuery.
- when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId)).thenAnswer((Invocation invocation) {
- return Future<List<BuilderStatistic>>.value(frameworkTestResponse);
- });
- // When creates issue
- when(mockIssuesService.create(captureAny, captureAny)).thenAnswer((_) {
- return Future<Issue>.value(Issue(htmlUrl: expectedSemanticsIntegrationTestNewIssueURL));
- });
- final Map<String, dynamic> result = await utf8.decoder
- .bind((await tester.get<Body>(handler)).serialize() as Stream<List<int>>)
- .transform(json.decoder)
- .single as Map<String, dynamic>;
-
- // Verify issue is created correctly.
- final List<dynamic> captured = verify(mockIssuesService.create(captureAny, captureAny)).captured;
- expect(captured.length, 2);
- expect(captured[0].toString(), Config.flutterSlug.toString());
- expect(captured[1], isA<IssueRequest>());
- final IssueRequest issueRequest = captured[1] as IssueRequest;
- expect(issueRequest.assignee, expectedFrameworkTestResponseAssignee);
- expect(const ListEquality<String>().equals(issueRequest.labels, expectedFrameworkTestResponseLabels), isTrue);
- // Verify no pr is created.
- verifyNever(mockPullRequestsService.create(captureAny, captureAny));
-
- expect(result['Status'], 'success');
- });
-
- test('Do not create issue if there is already one', () async {
- // When queries flaky data from BigQuery.
- when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId)).thenAnswer((Invocation invocation) {
- return Future<List<BuilderStatistic>>.value(semanticsIntegrationTestResponse);
- });
- // when gets existing flaky issues.
- when(mockIssuesService.listByRepo(captureAny, state: captureAnyNamed('state'), labels: captureAnyNamed('labels')))
- .thenAnswer((Invocation invocation) {
- return Stream<Issue>.fromIterable(<Issue>[
- Issue(
- title: expectedSemanticsIntegrationTestResponseTitle,
- body: expectedSemanticsIntegrationTestResponseBody,
- ),
- ]);
- });
- // When creates git tree
- when(mockGitService.createTree(captureAny, captureAny)).thenAnswer((_) {
- return Future<GitTree>.value(GitTree(expectedSemanticsIntegrationTestTreeSha, '', false, <GitTreeEntry>[]));
- });
- // When creates git commit
- when(mockGitService.createCommit(captureAny, captureAny)).thenAnswer((_) {
- return Future<GitCommit>.value(GitCommit(sha: expectedSemanticsIntegrationTestTreeSha));
- });
- // When creates git reference
- when(mockGitService.createReference(captureAny, captureAny, captureAny)).thenAnswer((Invocation invocation) {
- return Future<GitReference>.value(GitReference(ref: invocation.positionalArguments[1] as String?));
- });
- // When creates pr to mark test flaky
- when(mockPullRequestsService.create(captureAny, captureAny)).thenAnswer((_) {
- return Future<PullRequest>.value(PullRequest(number: expectedSemanticsIntegrationTestPRNumber));
- });
- final Map<String, dynamic> result = await utf8.decoder
- .bind((await tester.get<Body>(handler)).serialize() as Stream<List<int>>)
- .transform(json.decoder)
- .single as Map<String, dynamic>;
- // Verify no issue is created.
- verifyNever(mockIssuesService.create(captureAny, captureAny));
- // Verify no pr is created.
- verifyNever(mockPullRequestsService.create(captureAny, captureAny));
- expect(result['Status'], 'success');
- });
-
- test('Do not create issue if there is a recently closed one', () async {
- // When queries flaky data from BigQuery.
- when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId)).thenAnswer((Invocation invocation) {
- return Future<List<BuilderStatistic>>.value(semanticsIntegrationTestResponse);
- });
- // when get existing flaky issues.
- when(mockIssuesService.listByRepo(captureAny, state: captureAnyNamed('state'), labels: captureAnyNamed('labels')))
- .thenAnswer((Invocation invocation) {
- return Stream<Issue>.fromIterable(<Issue>[
- Issue(
- title: expectedSemanticsIntegrationTestResponseTitle,
- body: expectedSemanticsIntegrationTestResponseBody,
- state: 'closed',
- closedAt: DateTime.now().subtract(const Duration(days: kGracePeriodForClosedFlake - 1)),
- ),
- ]);
- });
- // When creates git tree
- when(mockGitService.createTree(captureAny, captureAny)).thenAnswer((_) {
- return Future<GitTree>.value(GitTree(expectedSemanticsIntegrationTestTreeSha, '', false, <GitTreeEntry>[]));
- });
- // When creates git commit
- when(mockGitService.createCommit(captureAny, captureAny)).thenAnswer((_) {
- return Future<GitCommit>.value(GitCommit(sha: expectedSemanticsIntegrationTestTreeSha));
- });
- // When creates git reference
- when(mockGitService.createReference(captureAny, captureAny, captureAny)).thenAnswer((Invocation invocation) {
- return Future<GitReference>.value(GitReference(ref: invocation.positionalArguments[1] as String?));
- });
- // When creates pr to mark test flaky
- when(mockPullRequestsService.create(captureAny, captureAny)).thenAnswer((_) {
- return Future<PullRequest>.value(PullRequest(number: expectedSemanticsIntegrationTestPRNumber));
- });
- final Map<String, dynamic> result = await utf8.decoder
- .bind((await tester.get<Body>(handler)).serialize() as Stream<List<int>>)
- .transform(json.decoder)
- .single as Map<String, dynamic>;
- // Verify no issue is created.
- verifyNever(mockIssuesService.create(captureAny, captureAny));
- // Verify no pr is created.
- verifyNever(mockPullRequestsService.create(captureAny, captureAny));
- expect(result['Status'], 'success');
- });
-
- test('Do create issue if there is a closed one outside the grace period', () async {
- // When queries flaky data from BigQuery.
- when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId)).thenAnswer((Invocation invocation) {
- return Future<List<BuilderStatistic>>.value(semanticsIntegrationTestResponse);
- });
- // when get existing flaky issues.
- when(mockIssuesService.listByRepo(captureAny, state: captureAnyNamed('state'), labels: captureAnyNamed('labels')))
- .thenAnswer((Invocation invocation) {
- return Stream<Issue>.fromIterable(<Issue>[
- Issue(
- title: expectedSemanticsIntegrationTestResponseTitle,
- body: expectedSemanticsIntegrationTestResponseBody,
- state: 'closed',
- closedAt: DateTime.now().subtract(const Duration(days: kGracePeriodForClosedFlake + 1)),
- ),
- ]);
- });
- // When creates git tree
- when(mockGitService.createTree(captureAny, captureAny)).thenAnswer((_) {
- return Future<GitTree>.value(GitTree(expectedSemanticsIntegrationTestTreeSha, '', false, <GitTreeEntry>[]));
- });
- // Add issue labels
- when(mockIssuesService.addLabelsToIssue(captureAny, captureAny, captureAny)).thenAnswer((_) {
- return Future<List<IssueLabel>>.value(<IssueLabel>[]);
- });
- // When creates git commit
- when(mockGitService.createCommit(captureAny, captureAny)).thenAnswer((_) {
- return Future<GitCommit>.value(GitCommit(sha: expectedSemanticsIntegrationTestTreeSha));
- });
- // When creates git reference
- when(mockGitService.createReference(captureAny, captureAny, captureAny)).thenAnswer((Invocation invocation) {
- return Future<GitReference>.value(GitReference(ref: invocation.positionalArguments[1] as String?));
- });
- // When creates pr to mark test flaky
- when(mockPullRequestsService.create(captureAny, captureAny)).thenAnswer((_) {
- return Future<PullRequest>.value(PullRequest(number: expectedSemanticsIntegrationTestPRNumber));
- });
- final Map<String, dynamic> result = await utf8.decoder
- .bind((await tester.get<Body>(handler)).serialize() as Stream<List<int>>)
- .transform(json.decoder)
- .single as Map<String, dynamic>;
- // Verify issue is created correctly.
- final List<dynamic> captured = verify(mockIssuesService.create(captureAny, captureAny)).captured;
- expect(captured.length, 2);
- expect(captured[0].toString(), Config.flutterSlug.toString());
- expect(captured[1], isA<IssueRequest>());
- final IssueRequest issueRequest = captured[1] as IssueRequest;
- expect(issueRequest.title, expectedSemanticsIntegrationTestResponseTitle);
- expect(issueRequest.body, expectedSemanticsIntegrationTestResponseBody);
- expect(issueRequest.assignee, expectedSemanticsIntegrationTestResponseAssignee);
- expect(
- const ListEquality<String>().equals(issueRequest.labels, expectedSemanticsIntegrationTestResponseLabels),
- isTrue,
- );
-
- expect(result['Status'], 'success');
- });
-
- test('Do not create an issue or PR if the test is already flaky', () async {
- // When queries flaky data from BigQuery.
- when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId)).thenAnswer((Invocation invocation) {
- return Future<List<BuilderStatistic>>.value(semanticsIntegrationTestResponse);
- });
- // when gets the content of .ci.yaml
- when(
- mockRepositoriesService.getContents(
- captureAny,
- kCiYamlPath,
- ),
- ).thenAnswer((Invocation invocation) {
- return Future<RepositoryContents>.value(
- RepositoryContents(file: GitHubFile(content: gitHubEncode(ciYamlContentAlreadyFlaky))),
- );
- });
-
- final Map<String, dynamic> result = await utf8.decoder
- .bind((await tester.get<Body>(handler)).serialize() as Stream<List<int>>)
- .transform(json.decoder)
- .single as Map<String, dynamic>;
- // Verify no issue is created.
- verifyNever(mockIssuesService.create(captureAny, captureAny));
- // Verify no pr is created.
- verifyNever(mockPullRequestsService.create(captureAny, captureAny));
-
- expect(result['Status'], 'success');
- });
-
- test('Do not create PR if there is already an opened one', () async {
- // When queries flaky data from BigQuery.
- when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId)).thenAnswer((Invocation invocation) {
- return Future<List<BuilderStatistic>>.value(semanticsIntegrationTestResponse);
- });
- // when gets existing marks flaky prs.
- when(mockPullRequestsService.list(captureAny)).thenAnswer((Invocation invocation) {
- return Stream<PullRequest>.fromIterable(<PullRequest>[
- PullRequest(
- title: expectedSemanticsIntegrationTestPullRequestTitle,
- body: expectedSemanticsIntegrationTestPullRequestBody,
- state: 'open',
- ),
- ]);
- });
-
- final Map<String, dynamic> result = await utf8.decoder
- .bind((await tester.get<Body>(handler)).serialize() as Stream<List<int>>)
- .transform(json.decoder)
- .single as Map<String, dynamic>;
- // Verify no pr is created.
- verifyNever(mockPullRequestsService.create(captureAny, captureAny));
-
- expect(result['Status'], 'success');
- });
- });
-
- test('retrieveMetaTagsFromContent can work with different newlines', () async {
- const String differentNewline =
- '<!-- meta-tags: To be used by the automation script only, DO NOT MODIFY.\r\n{"name": "Mac_android android_semantics_integration_test"}\r\n-->';
- final Map<String, dynamic> metaTags = retrieveMetaTagsFromContent(differentNewline)!;
- expect(metaTags['name'], 'Mac_android android_semantics_integration_test');
- });
-
- test('getIgnoreFlakiness handles non-existing builderame', () async {
- final YamlMap? ci = loadYaml(ciYamlContent) as YamlMap?;
- final pb.SchedulerConfig unCheckedSchedulerConfig = pb.SchedulerConfig()..mergeFromProto3Json(ci);
- final CiYaml ciYaml = CiYaml(
- slug: Config.flutterSlug,
- branch: Config.defaultBranch(Config.flutterSlug),
- config: unCheckedSchedulerConfig,
- );
- expect(FileFlakyIssueAndPR.getIgnoreFlakiness('Non_existing', ciYaml), false);
- });
-}
diff --git a/app_dart/test/request_handlers/file_flaky_issue_and_pr_test_data.dart b/app_dart/test/request_handlers/file_flaky_issue_and_pr_test_data.dart
deleted file mode 100644
index 5ede4c0..0000000
--- a/app_dart/test/request_handlers/file_flaky_issue_and_pr_test_data.dart
+++ /dev/null
@@ -1,384 +0,0 @@
-// Copyright 2019 The Flutter 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:convert';
-
-import 'package:cocoon_service/src/service/bigquery.dart';
-
-const String ciYamlContent = '''
-# Describes the targets run in continuous integration environment.
-#
-# Flutter infra uses this file to generate a checklist of tasks to be performed
-# for every commit.
-#
-# More information at:
-# * https://github.com/flutter/cocoon/blob/master/scheduler/README.md
-enabled_branches:
- - master
-
-targets:
- - name: Mac_android android_semantics_integration_test
- builder: Mac_android android_semantics_integration_test
- presubmit: false
- scheduler: luci
- properties:
- tags: >
- ["devicelab"]
- task_name: android_semantics_integration_test
- - name: Mac_android ignore_myflakiness
- bringup: true
- builder: Mac_android ignore_myflakiness
- presubmit: false
- scheduler: luci
- properties:
- ignore_flakiness: "true"
- tags: >
- ["devicelab"]
- task_name: ignore_myflakiness
- - name: Linux analyze
- builder: Linux analyze
- scheduler: luci
- properties:
- tags: >
- ["framework","hostonly"]
- - name: Windows framework_tests_misc
- builder: Windows framework_tests_misc
- presubmit: false
- scheduler: luci
- properties:
- tags: >
- ["shard"]
-''';
-
-const String ciYamlContentTwoFlakyTargets = '''
-# Describes the targets run in continuous integration environment.
-#
-# Flutter infra uses this file to generate a checklist of tasks to be performed
-# for every commit.
-#
-# More information at:
-# * https://github.com/flutter/cocoon/blob/master/scheduler/README.md
-enabled_branches:
- - master
-
-targets:
- - name: Mac_android android_semantics_integration_test
- builder: Mac_android android_semantics_integration_test
- presubmit: false
- scheduler: luci
- properties:
- tags: >
- ["devicelab"]
- task_name: android_semantics_integration_test
- - name: Mac_android ignore_myflakiness
- builder: Mac_android ignore_myflakiness
- presubmit: false
- scheduler: luci
- properties:
- tags: >
- ["devicelab"]
- task_name: ignore_myflakiness
- - name: Linux analyze
- builder: Linux analyze
- scheduler: luci
- properties:
- tags: >
- ["framework","hostonly"]
- - name: Windows framework_tests_misc
- builder: Windows framework_tests_misc
- presubmit: false
- scheduler: luci
- properties:
- tags: >
- ["shard"]
-''';
-
-const String ciYamlContentAlreadyFlaky = '''
-# Describes the targets run in continuous integration environment.
-#
-# Flutter infra uses this file to generate a checklist of tasks to be performed
-# for every commit.
-#
-# More information at:
-# * https://github.com/flutter/cocoon/blob/master/scheduler/README.md
-enabled_branches:
- - master
-
-targets:
- - name: Mac_android android_semantics_integration_test
- builder: Mac_android android_semantics_integration_test
- bringup: true
- presubmit: false
- scheduler: luci
- properties:
- tags: >
- ["devicelab"]
- task_name: android_semantics_integration_test
- - name: Mac_android ignore_myflakiness
- bringup: true
- builder: Mac_android ignore_myflakiness
- presubmit: false
- scheduler: luci
- properties:
- ignore_flakiness: "true"
- tags: >
- ["devicelab"]
- - name: Linux analyze
- builder: Linux analyze
- scheduler: luci
- properties:
- tags: >
- ["framework","hostonly"]
- - name: Windows framework_tests_misc
- builder: Windows framework_tests_misc
- presubmit: false
- scheduler: luci
- properties:
- tags: >
- ["shard"]
-''';
-
-const String testOwnersContent = '''
-
-# Below is a list of Flutter team members' GitHub handles who are
-# test owners of this repository.
-#
-# These owners are mainly team leaders and their sub-teams. Please feel
-# free to claim ownership by adding your handle to corresponding tests.
-#
-# This file will be used as a reference when new flaky bugs are filed and
-# the TL will be assigned and the sub-team will be labeled by default
-# for further triage.
-
-## Linux Android DeviceLab tests
-/dev/devicelab/bin/tasks/android_semantics_integration_test.dart @HansMuller @flutter/framework
-
-## Host only framework tests
-# Linux analyze
-/dev/bots/analyze.dart @HansMuller @flutter/framework
-
-## Firebase tests
-/dev/integration_tests/abstract_method_smoke_test @blasten @flutter/android
-
-## Shards tests
-# framework_tests @HansMuller @flutter/framework
-
-## Mac Android DeviceLab tests
-/dev/devicelab/bin/tasks/android_semantics_integration_test.dart @HansMuller @flutter/framework
-''';
-
-const String jobNotCompleteResponse = '''
-{
- "jobComplete" : false
-}
-''';
-
-const String expectedSemanticsIntegrationTestNewIssueURL = 'https://something.something';
-const String expectedSemanticsIntegrationTestTreeSha = 'abcdefg';
-const int expectedSemanticsIntegrationTestPRNumber = 123;
-
-final List<BuilderStatistic> semanticsIntegrationTestResponse = <BuilderStatistic>[
- BuilderStatistic(
- name: 'Mac_android android_semantics_integration_test',
- flakyRate: 0.5,
- flakyBuilds: <String>['103', '102', '101'],
- succeededBuilds: <String>['203', '202', '201'],
- recentCommit: 'abc',
- flakyBuildOfRecentCommit: '103',
- flakyNumber: 3,
- totalNumber: 6,
- ),
- // This builder is flakey, but it should be
- // ignored because it has ignore_flakiness set.
- BuilderStatistic(
- name: 'Mac_android ignore_myflakiness',
- flakyRate: 0.5,
- flakyBuilds: <String>['103', '102', '101'],
- succeededBuilds: <String>['203', '202', '201'],
- recentCommit: 'abc',
- flakyBuildOfRecentCommit: '103',
- flakyNumber: 3,
- totalNumber: 6,
- ),
-];
-
-const String expectedSemanticsIntegrationTestResponseTitle =
- 'Mac_android android_semantics_integration_test is 50.00% flaky';
-const String expectedSemanticsIntegrationTestResponseBody = '''
-<!-- meta-tags: To be used by the automation script only, DO NOT MODIFY.
-{
- "name": "Mac_android android_semantics_integration_test"
-}
--->
-
-The post-submit test builder `Mac_android android_semantics_integration_test` had a flaky ratio 50.00% for the past (up to) 100 commits, which is above our 2.00% threshold.
-
-One recent flaky example for a same commit: https://ci.chromium.org/ui/p/flutter/builders/prod/Mac_android%20android_semantics_integration_test/103
-Commit: https://github.com/flutter/flutter/commit/abc
-
-Flaky builds:
-https://ci.chromium.org/ui/p/flutter/builders/prod/Mac_android%20android_semantics_integration_test/103
-https://ci.chromium.org/ui/p/flutter/builders/prod/Mac_android%20android_semantics_integration_test/102
-https://ci.chromium.org/ui/p/flutter/builders/prod/Mac_android%20android_semantics_integration_test/101
-
-Recent test runs:
-https://flutter-dashboard.appspot.com/#/build?taskFilter=Mac_android%20android_semantics_integration_test
-
-Please follow https://github.com/flutter/flutter/wiki/Reducing-Test-Flakiness#fixing-flaky-tests to fix the flakiness and enable the test back after validating the fix (internal dashboard to validate: go/flutter_test_flakiness).
-''';
-
-const String expectedSemanticsIntegrationTestResponseAssignee = 'HansMuller';
-const List<String> expectedSemanticsIntegrationTestResponseLabels = <String>[
- 'c: flake',
- 'P0',
- 'team-framework',
-];
-const String expectedSemanticsIntegrationTestCiYamlContent = '''
-# Describes the targets run in continuous integration environment.
-#
-# Flutter infra uses this file to generate a checklist of tasks to be performed
-# for every commit.
-#
-# More information at:
-# * https://github.com/flutter/cocoon/blob/master/scheduler/README.md
-enabled_branches:
- - master
-
-targets:
- - name: Mac_android android_semantics_integration_test
- bringup: true # Flaky $expectedSemanticsIntegrationTestNewIssueURL
- builder: Mac_android android_semantics_integration_test
- presubmit: false
- scheduler: luci
- properties:
- tags: >
- ["devicelab"]
- task_name: android_semantics_integration_test
- - name: Mac_android ignore_myflakiness
- bringup: true
- builder: Mac_android ignore_myflakiness
- presubmit: false
- scheduler: luci
- properties:
- ignore_flakiness: "true"
- tags: >
- ["devicelab"]
- task_name: ignore_myflakiness
- - name: Linux analyze
- builder: Linux analyze
- scheduler: luci
- properties:
- tags: >
- ["framework","hostonly"]
- - name: Windows framework_tests_misc
- builder: Windows framework_tests_misc
- presubmit: false
- scheduler: luci
- properties:
- tags: >
- ["shard"]
-''';
-const String expectedSemanticsIntegrationTestPullRequestTitle =
- 'Marks Mac_android android_semantics_integration_test to be flaky';
-const String expectedSemanticsIntegrationTestPullRequestBody = '''
-<!-- meta-tags: To be used by the automation script only, DO NOT MODIFY.
-{
- "name": "Mac_android android_semantics_integration_test"
-}
--->
-Issue link: $expectedSemanticsIntegrationTestNewIssueURL
-''';
-
-final List<BuilderStatistic> limitedNumberOfBuildsResponse = <BuilderStatistic>[
- BuilderStatistic(
- name: 'Mac_android android_semantics_integration_test',
- flakyRate: 0.25,
- flakyBuilds: <String>['103', '102', '101'],
- succeededBuilds: <String>['201'],
- recentCommit: 'abc',
- flakyBuildOfRecentCommit: '103',
- flakyNumber: 1,
- totalNumber: 4,
- ),
-];
-
-const String expectedLimitedNumberOfBuildsResponseBody = '''
-<!-- meta-tags: To be used by the automation script only, DO NOT MODIFY.
-{
- "name": "Mac_android android_semantics_integration_test"
-}
--->
-
-The post-submit test builder `Mac_android android_semantics_integration_test` had a flaky ratio 25.00% for the past (up to) 100 commits, which is above our 2.00% threshold.
-
-One recent flaky example for a same commit: https://ci.chromium.org/ui/p/flutter/builders/prod/Mac_android%20android_semantics_integration_test/103
-Commit: https://github.com/flutter/flutter/commit/abc
-
-Flaky builds:
-https://ci.chromium.org/ui/p/flutter/builders/prod/Mac_android%20android_semantics_integration_test/103
-https://ci.chromium.org/ui/p/flutter/builders/prod/Mac_android%20android_semantics_integration_test/102
-https://ci.chromium.org/ui/p/flutter/builders/prod/Mac_android%20android_semantics_integration_test/101
-
-Recent test runs:
-https://flutter-dashboard.appspot.com/#/build?taskFilter=Mac_android%20android_semantics_integration_test
-
-Please follow https://github.com/flutter/flutter/wiki/Reducing-Test-Flakiness#fixing-flaky-tests to fix the flakiness and enable the test back after validating the fix (internal dashboard to validate: go/flutter_test_flakiness).
-''';
-
-final List<BuilderStatistic> analyzeTestResponse = <BuilderStatistic>[
- BuilderStatistic(
- name: 'Linux analyze',
- flakyRate: 0.03,
- flakyBuilds: <String>['103', '102', '101'],
- succeededBuilds: <String>['203', '202', '201'],
- recentCommit: 'abc',
- flakyBuildOfRecentCommit: '103',
- flakyNumber: 3,
- totalNumber: 6,
- ),
-];
-const String expectedAnalyzeTestResponseAssignee = 'HansMuller';
-const List<String> expectedAnalyzeTestResponseLabels = <String>[
- 'c: flake',
- 'P0',
- 'team-framework',
-];
-
-final List<BuilderStatistic> frameworkTestResponse = <BuilderStatistic>[
- BuilderStatistic(
- name: 'Windows framework_tests_misc',
- flakyRate: 0.03,
- flakyBuilds: <String>['103', '102', '101'],
- succeededBuilds: <String>['203', '202', '201'],
- recentCommit: 'abc',
- flakyBuildOfRecentCommit: '103',
- flakyNumber: 3,
- totalNumber: 6,
- ),
-];
-
-final List<BuilderStatistic> unknownTestResponse = <BuilderStatistic>[
- BuilderStatistic(
- name: 'Windows some_unknown_test',
- flakyRate: 0.03,
- flakyBuilds: <String>['103', '102', '101'],
- succeededBuilds: <String>['203', '202', '201'],
- recentCommit: 'abc',
- flakyBuildOfRecentCommit: '103',
- flakyNumber: 3,
- totalNumber: 6,
- ),
-];
-const String expectedFrameworkTestResponseAssignee = 'HansMuller';
-const List<String> expectedFrameworkTestResponseLabels = <String>[
- 'c: flake',
- 'P0',
- 'team-framework',
-];
-
-String gitHubEncode(String source) {
- final List<int> utf8Characters = utf8.encode(source);
- final String base64encoded = base64Encode(utf8Characters);
- return base64encoded;
-}
diff --git a/app_dart/test/request_handlers/flaky_handler_utiles_test.dart b/app_dart/test/request_handlers/flaky_handler_utiles_test.dart
deleted file mode 100644
index 81645c4..0000000
--- a/app_dart/test/request_handlers/flaky_handler_utiles_test.dart
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright 2020 The Flutter 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 'package:cocoon_service/protos.dart' as pb;
-import 'package:cocoon_service/src/request_handlers/flaky_handler_utils.dart';
-import 'package:cocoon_service/src/service/config.dart';
-import 'package:cocoon_service/src/service/github_service.dart';
-import 'package:github/github.dart' hide Team;
-import 'package:mockito/mockito.dart';
-import 'package:test/test.dart';
-
-import '../src/datastore/fake_config.dart';
-import '../src/utilities/mocks.dart';
-
-void main() {
- group('Gets test ownership', () {
- String testOwnersContent;
-
- group('framework host only', () {
- test('returns correct owner when no mulitple tests share the same file', () async {
- final pb.Target target = pb.Target(name: 'Linux abc');
- testOwnersContent = '''
-## Host only framework tests
-# Linux abc
-abc_test.sh @ghi @flutter/engine
-## Firebase tests
-''';
- final TestOwnership ownership = getTestOwnership(target, BuilderType.frameworkHostOnly, testOwnersContent);
- expect(ownership.owner, 'ghi');
- expect(ownership.team, Team.engine);
- });
-
- test('returns correct owner when mulitple tests share the same file', () async {
- final pb.Target target1 = pb.Target(name: 'Linux abc');
- final pb.Target target2 = pb.Target(name: 'Linux def');
- testOwnersContent = '''
-## Host only framework tests
-# Linux abc
-# Linu def
-abc_test.sh @ghi @flutter/framework
-## Firebase tests
-''';
- final TestOwnership ownership1 = getTestOwnership(target1, BuilderType.frameworkHostOnly, testOwnersContent);
- expect(ownership1.owner, 'ghi');
- expect(ownership1.team, Team.framework);
- final TestOwnership ownership2 = getTestOwnership(target2, BuilderType.frameworkHostOnly, testOwnersContent);
- expect(ownership2.owner, 'ghi');
- expect(ownership2.team, Team.framework);
- });
- });
-
- group('firebaselab only', () {
- test('returns correct owner', () async {
- final pb.Target target = pb.Target(name: 'Linux firebase_abc');
- testOwnersContent = '''
-## Firebase tests
-# Linux abc
-/test/abc @def @flutter/tool
-## Shards tests
-''';
- final TestOwnership ownership = getTestOwnership(target, BuilderType.firebaselab, testOwnersContent);
- expect(ownership.owner, 'def');
- expect(ownership.team, Team.tool);
- });
- });
-
- group('devicelab tests', () {
- test('returns correct owner', () async {
- final pb.Target target = pb.Target(name: 'abc', properties: {'task_name': 'abc'});
- testOwnersContent = '''
-## Linux Android DeviceLab tests
-/dev/devicelab/bin/tasks/abc.dart @def @flutter/web
-
-## Host only framework tests
-''';
- final TestOwnership ownership = getTestOwnership(target, BuilderType.devicelab, testOwnersContent);
- expect(ownership.owner, 'def');
- expect(ownership.team, Team.web);
- });
- });
-
- group('shards tests', () {
- test('returns correct owner', () async {
- final pb.Target target = pb.Target(name: 'Linux abc');
- testOwnersContent = '''
-## Shards tests
-#
-# abc @def @flutter/engine
-''';
- final TestOwnership ownership = getTestOwnership(target, BuilderType.shard, testOwnersContent);
- expect(ownership.owner, 'def');
- expect(ownership.team, Team.engine);
- });
- });
-
- group('getExistingPRs', () {
- test('throws more detailed logs for bad prs with inappropriate body meta tag', () async {
- final MockGitHub mockGitHubClient = MockGitHub();
- final MockPullRequestsService mockPullRequestsService = MockPullRequestsService();
- const String expectedHtml = 'https://someurl';
- // when gets existing marks flaky prs.
- when(mockPullRequestsService.list(captureAny)).thenAnswer((Invocation invocation) {
- return Stream<PullRequest>.value(
- PullRequest(
- htmlUrl: expectedHtml,
- body: '''
-<!-- meta-tags: To be used by the automation script only, DO NOT MODIFY.
-{
- "name"
-}
--->''',
- ),
- );
- });
- when(mockGitHubClient.pullRequests).thenReturn(mockPullRequestsService);
- final FakeConfig config = FakeConfig(
- githubService: GithubService(mockGitHubClient),
- );
- expect(
- () => getExistingPRs(config.githubService!, Config.flutterSlug),
- throwsA(
- predicate<String>((String e) {
- return e.contains('Unable to parse body of $expectedHtml');
- }),
- ),
- );
- });
-
- test('handles PRs with empty body message', () async {
- final MockGitHub mockGitHubClient = MockGitHub();
- final MockPullRequestsService mockPullRequestsService = MockPullRequestsService();
- const String expectedHtml = 'https://someurl';
- when(mockPullRequestsService.list(captureAny)).thenAnswer((Invocation invocation) {
- return Stream<PullRequest>.value(
- PullRequest(
- htmlUrl: expectedHtml,
- ),
- );
- });
- when(mockGitHubClient.pullRequests).thenReturn(mockPullRequestsService);
- final FakeConfig config = FakeConfig(
- githubService: GithubService(mockGitHubClient),
- );
- expect(await getExistingPRs(config.githubService!, Config.flutterSlug), <String?, PullRequest>{});
- });
- });
- });
- group('Gets team label', () {
- test('returns correct label when matched', () async {
- expect(getTeamLabelFromTeam(Team.infra), kInfraLabel);
- });
-
- test('returns null when not matched', () async {
- expect(getTeamLabelFromTeam(Team.unknown), null);
- });
- });
-}
diff --git a/app_dart/test/request_handlers/flush_cache_test.dart b/app_dart/test/request_handlers/flush_cache_test.dart
deleted file mode 100644
index d7c0d99..0000000
--- a/app_dart/test/request_handlers/flush_cache_test.dart
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright 2019 The Flutter 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:typed_data';
-
-import 'package:cocoon_service/src/request_handlers/flush_cache.dart';
-import 'package:cocoon_service/src/request_handling/exceptions.dart';
-import 'package:cocoon_service/src/service/cache_service.dart';
-import 'package:cocoon_service/src/service/config.dart';
-import 'package:test/test.dart';
-
-import '../src/datastore/fake_config.dart';
-import '../src/request_handling/api_request_handler_tester.dart';
-import '../src/request_handling/fake_authentication.dart';
-import '../src/request_handling/fake_http.dart';
-
-void main() {
- group('FlushCache', () {
- FakeConfig config;
- late ApiRequestHandlerTester tester;
- late FlushCache handler;
- late CacheService cache;
-
- setUp(() {
- tester = ApiRequestHandlerTester();
- cache = CacheService(inMemory: true);
- config = FakeConfig();
- handler = FlushCache(
- config: config,
- authenticationProvider: FakeAuthenticationProvider(),
- cache: cache,
- );
- });
-
- test('cache is empty when given an existing config key', () async {
- const String cacheKey = 'test';
- await cache.set(
- Config.configCacheName,
- cacheKey,
- Uint8List.fromList('123'.codeUnits),
- );
-
- tester.request = FakeHttpRequest(
- queryParametersValue: <String, String>{
- FlushCache.cacheKeyParam: cacheKey,
- },
- );
- await tester.get(handler);
-
- expect(await cache.getOrCreate(Config.configCacheName, cacheKey, createFn: null), null);
- });
-
- test('raises error if cache key not passed', () async {
- expect(tester.get(handler), throwsA(isA<BadRequestException>()));
- });
-
- test('raises error if cache key does not exist', () async {
- tester.request = FakeHttpRequest(
- queryParametersValue: <String, String>{
- FlushCache.cacheKeyParam: 'abc',
- },
- );
- expect(tester.get(handler), throwsA(isA<NotFoundException>()));
- });
- });
-}
diff --git a/app_dart/test/request_handlers/get_green_commits_test.dart b/app_dart/test/request_handlers/get_green_commits_test.dart
deleted file mode 100644
index 6e9df50..0000000
--- a/app_dart/test/request_handlers/get_green_commits_test.dart
+++ /dev/null
@@ -1,199 +0,0 @@
-// Copyright 2021 The Flutter 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:convert';
-
-import 'package:cocoon_service/cocoon_service.dart';
-import 'package:cocoon_service/src/model/appengine/commit.dart';
-import 'package:cocoon_service/src/model/appengine/stage.dart';
-import 'package:cocoon_service/src/model/appengine/task.dart';
-import 'package:cocoon_service/src/service/build_status_provider.dart';
-import 'package:cocoon_service/src/service/datastore.dart';
-import 'package:gcloud/db.dart';
-import 'package:test/test.dart';
-
-import '../src/datastore/fake_config.dart';
-import '../src/request_handling/fake_authentication.dart';
-import '../src/request_handling/fake_http.dart';
-import '../src/request_handling/request_handler_tester.dart';
-import '../src/service/fake_build_status_provider.dart';
-import '../src/utilities/entity_generators.dart';
-
-void main() {
- group('GetGreenCommits', () {
- late FakeConfig config;
- FakeClientContext clientContext;
- FakeKeyHelper keyHelper;
- FakeBuildStatusService buildStatusService;
- late RequestHandlerTester tester;
- late GetGreenCommits handler;
-
- final Commit commit1 = generateCommit(1, timestamp: 3, sha: 'ea28a9c34dc701de891eaf74503ca4717019f829');
- final Commit commit2 = generateCommit(2, timestamp: 1, sha: 'd5b0b3c8d1c5fd89302089077ccabbcfaae045e4');
- final Commit commitBranched = generateCommit(
- 2,
- timestamp: 1,
- sha: 'ffffffffffffffffffffffffffffffffaae045e4',
- branch: 'flutter-2.13-candidate.0',
- );
-
- final Task task1Succeed = generateTask(1, status: Task.statusSucceeded);
- final Task task2Failed = generateTask(2, status: Task.statusFailed); // should fail if included
- final Task task3FailedFlaky =
- generateTask(3, status: Task.statusFailed, isFlaky: true); // should succeed if included because `bringup: true`
- final Task task4SucceedFlaky = generateTask(4, status: Task.statusSucceeded, isFlaky: true);
-
- final Stage stageOneSucceed =
- Stage('cocoon', commit1, [task1Succeed], Task.statusInProgress); // should scceed, since task 1 succeed
- final Stage stageFailed = Stage(
- 'luci',
- commit1,
- [task1Succeed, task2Failed],
- Task.statusInProgress,
- ); // should fail, since task 1 succeed and task2 fail
- final Stage stageMultipleSucceed = Stage(
- 'cocoon',
- commit2,
- [task1Succeed, task4SucceedFlaky],
- Task.statusInProgress,
- ); // should succeed, since both task 1 and task 4 succeed
- final Stage stageFailedFlaky = Stage(
- 'luci',
- commit2,
- [task1Succeed, task3FailedFlaky],
- Task.statusInProgress,
- ); // should succeed, even though it includes task 3
-
- Future<List<T?>?> decodeHandlerBody<T>() async {
- final Body body = await tester.get(handler);
- return (await utf8.decoder.bind(body.serialize() as Stream<List<int>>).transform(json.decoder).single
- as List<dynamic>)
- .cast<T>();
- }
-
- setUp(() {
- clientContext = FakeClientContext();
- keyHelper = FakeKeyHelper(applicationContext: clientContext.applicationContext);
- tester = RequestHandlerTester();
- config = FakeConfig(keyHelperValue: keyHelper);
- buildStatusService = FakeBuildStatusService(commitStatuses: <CommitStatus>[]);
- handler = GetGreenCommits(
- config: config,
- datastoreProvider: (DatastoreDB db) => DatastoreService(config.db, 5),
- buildStatusProvider: (_) => buildStatusService,
- );
- });
-
- test('no green commits', () async {
- final List<String?> result = (await decodeHandlerBody())!;
- expect(result, isEmpty);
- });
-
- test('should return commits with all tasks succeed', () async {
- buildStatusService = FakeBuildStatusService(
- commitStatuses: <CommitStatus>[
- CommitStatus(commit1, <Stage>[stageOneSucceed]),
- CommitStatus(commit2, <Stage>[stageOneSucceed, stageMultipleSucceed]),
- ],
- );
- handler = GetGreenCommits(
- config: config,
- datastoreProvider: (DatastoreDB db) => DatastoreService(config.db, 5),
- buildStatusProvider: (_) => buildStatusService,
- );
-
- final List<String?> result = (await decodeHandlerBody())!;
-
- expect(result.length, 2);
- expect(result, <String>[
- commit2.sha!,
- commit1.sha!,
- ]);
- });
-
- test('should fail commits that have failed task without [bringup: true] label', () async {
- buildStatusService = FakeBuildStatusService(
- commitStatuses: <CommitStatus>[
- CommitStatus(commit1, <Stage>[stageFailed]),
- CommitStatus(commit2, <Stage>[stageOneSucceed, stageMultipleSucceed]),
- ],
- );
- handler = GetGreenCommits(
- config: config,
- datastoreProvider: (DatastoreDB db) => DatastoreService(config.db, 5),
- buildStatusProvider: (_) => buildStatusService,
- );
-
- final List<String?> result = (await decodeHandlerBody())!;
-
- expect(result.length, 1);
- expect(result, <String>[commit2.sha!]);
- });
-
- test('should return commits with failed tasks but with `bringup: true` label', () async {
- buildStatusService = FakeBuildStatusService(
- commitStatuses: <CommitStatus>[
- CommitStatus(commit1, <Stage>[stageFailed]),
- CommitStatus(commit2, <Stage>[stageFailedFlaky]),
- ],
- );
- handler = GetGreenCommits(
- config: config,
- datastoreProvider: (DatastoreDB db) => DatastoreService(config.db, 5),
- buildStatusProvider: (_) => buildStatusService,
- );
-
- final List<String?> result = (await decodeHandlerBody())!;
-
- expect(result.length, 1);
- expect(result, <String>[commit2.sha!]);
- });
-
- test('should return commits with both flaky and succeeded tasks', () async {
- buildStatusService = FakeBuildStatusService(
- commitStatuses: <CommitStatus>[
- CommitStatus(commit1, <Stage>[stageOneSucceed, stageMultipleSucceed]),
- CommitStatus(commit2, <Stage>[stageOneSucceed, stageFailedFlaky]),
- ],
- );
- handler = GetGreenCommits(
- config: config,
- datastoreProvider: (DatastoreDB db) => DatastoreService(config.db, 5),
- buildStatusProvider: (_) => buildStatusService,
- );
-
- final List<String?> result = (await decodeHandlerBody())!;
-
- expect(result.length, 2);
- expect(result, <String>[
- commit2.sha!,
- commit1.sha!,
- ]);
- });
-
- test('should return branched commits', () async {
- buildStatusService = FakeBuildStatusService(
- commitStatuses: <CommitStatus>[
- CommitStatus(commitBranched, <Stage>[stageOneSucceed]),
- ],
- );
- tester.request = FakeHttpRequest(
- queryParametersValue: <String, String>{
- GetGreenCommits.kBranchParam: commitBranched.branch!,
- },
- );
- handler = GetGreenCommits(
- config: config,
- datastoreProvider: (DatastoreDB db) => DatastoreService(config.db, 5),
- buildStatusProvider: (_) => buildStatusService,
- );
-
- final List<String?> result = (await decodeHandlerBody())!;
-
- expect(result, <String>[
- commitBranched.sha!,
- ]);
- });
- });
-}
diff --git a/app_dart/test/request_handlers/get_status_test.dart b/app_dart/test/request_handlers/get_status_test.dart
deleted file mode 100644
index 23bcd23..0000000
--- a/app_dart/test/request_handlers/get_status_test.dart
+++ /dev/null
@@ -1,191 +0,0 @@
-// Copyright 2019 The Flutter 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:convert';
-
-import 'package:cocoon_service/src/model/appengine/commit.dart';
-import 'package:cocoon_service/src/model/appengine/stage.dart';
-import 'package:cocoon_service/src/request_handlers/get_status.dart';
-import 'package:cocoon_service/src/request_handling/body.dart';
-import 'package:cocoon_service/src/service/build_status_provider.dart';
-import 'package:cocoon_service/src/service/datastore.dart';
-import 'package:gcloud/db.dart';
-import 'package:test/test.dart';
-
-import '../src/datastore/fake_config.dart';
-import '../src/request_handling/fake_authentication.dart';
-import '../src/request_handling/fake_http.dart';
-import '../src/request_handling/request_handler_tester.dart';
-import '../src/service/fake_build_status_provider.dart';
-
-void main() {
- group('GetStatus', () {
- late FakeConfig config;
- FakeClientContext clientContext;
- FakeKeyHelper keyHelper;
- FakeBuildStatusService buildStatusService;
- late RequestHandlerTester tester;
- late GetStatus handler;
-
- late Commit commit1;
- late Commit commit2;
-
- Future<T?> decodeHandlerBody<T>() async {
- final Body body = await tester.get(handler);
- return await utf8.decoder.bind(body.serialize() as Stream<List<int>>).transform(json.decoder).single as T?;
- }
-
- setUp(() {
- clientContext = FakeClientContext();
- keyHelper = FakeKeyHelper(applicationContext: clientContext.applicationContext);
- tester = RequestHandlerTester();
- config = FakeConfig(keyHelperValue: keyHelper);
- buildStatusService = FakeBuildStatusService(commitStatuses: <CommitStatus>[]);
- handler = GetStatus(
- config: config,
- datastoreProvider: (DatastoreDB db) => DatastoreService(config.db, 5),
- buildStatusProvider: (_) => buildStatusService,
- );
- commit1 = Commit(
- key: config.db.emptyKey.append(Commit, id: 'flutter/flutter/ea28a9c34dc701de891eaf74503ca4717019f829'),
- repository: 'flutter/flutter',
- sha: 'ea28a9c34dc701de891eaf74503ca4717019f829',
- timestamp: 3,
- message: 'test message 1',
- branch: 'master',
- );
- commit2 = Commit(
- key: config.db.emptyKey.append(Commit, id: 'flutter/flutter/d5b0b3c8d1c5fd89302089077ccabbcfaae045e4'),
- repository: 'flutter/flutter',
- sha: 'd5b0b3c8d1c5fd89302089077ccabbcfaae045e4',
- timestamp: 1,
- message: 'test message 2',
- branch: 'master',
- );
- });
-
- test('no statuses', () async {
- final Map<String, dynamic> result = (await decodeHandlerBody())!;
- expect(result['Statuses'], isEmpty);
- });
-
- test('reports statuses without input commit key', () async {
- config.db.values[commit1.key] = commit1;
- config.db.values[commit2.key] = commit2;
- buildStatusService = FakeBuildStatusService(
- commitStatuses: <CommitStatus>[CommitStatus(commit1, const <Stage>[]), CommitStatus(commit2, const <Stage>[])],
- );
- handler = GetStatus(
- config: config,
- datastoreProvider: (DatastoreDB db) => DatastoreService(config.db, 5),
- buildStatusProvider: (_) => buildStatusService,
- );
-
- final Map<String, dynamic> result = (await decodeHandlerBody())!;
-
- expect(result['Statuses'].length, 2);
- });
-
- test('reports statuses with input commit key', () async {
- final Commit commit1 = Commit(
- key: config.db.emptyKey.append(Commit, id: 'flutter/flutter/ea28a9c34dc701de891eaf74503ca4717019f829'),
- repository: 'flutter/flutter',
- sha: 'ea28a9c34dc701de891eaf74503ca4717019f829',
- timestamp: 3,
- message: 'test message 1',
- branch: 'master',
- );
- final Commit commit2 = Commit(
- key: config.db.emptyKey.append(Commit, id: 'flutter/flutter/d5b0b3c8d1c5fd89302089077ccabbcfaae045e4'),
- repository: 'flutter/flutter',
- sha: 'd5b0b3c8d1c5fd89302089077ccabbcfaae045e4',
- timestamp: 1,
- message: 'test message 2',
- branch: 'master',
- );
- config.db.values[commit1.key] = commit1;
- config.db.values[commit2.key] = commit2;
- buildStatusService = FakeBuildStatusService(
- commitStatuses: <CommitStatus>[CommitStatus(commit1, const <Stage>[]), CommitStatus(commit2, const <Stage>[])],
- );
- handler = GetStatus(
- config: config,
- datastoreProvider: (DatastoreDB db) => DatastoreService(config.db, 5),
- buildStatusProvider: (_) => buildStatusService,
- );
-
- const String expectedLastCommitKeyEncoded =
- 'ahNzfmZsdXR0ZXItZGFzaGJvYXJkckcLEglDaGVja2xpc3QiOGZsdXR0ZXIvZmx1dHRlci9lYTI4YTljMzRkYzcwMWRlODkxZWFmNzQ1MDNjYTQ3MTcwMTlmODI5DA';
-
- tester.request = FakeHttpRequest(
- queryParametersValue: <String, String>{
- GetStatus.kLastCommitKeyParam: expectedLastCommitKeyEncoded,
- },
- );
- final Map<String, dynamic> result = (await decodeHandlerBody())!;
-
- expect(result['Statuses'].first, <String, dynamic>{
- 'Checklist': <String, dynamic>{
- 'Key':
- 'ahFmbHV0dGVyLWRhc2hib2FyZHJHCxIJQ2hlY2tsaXN0IjhmbHV0dGVyL2ZsdXR0ZXIvZDViMGIzYzhkMWM1ZmQ4OTMwMjA4OTA3N2NjYWJiY2ZhYWUwNDVlNAyiAQlbZGVmYXVsdF0',
- 'Checklist': <String, dynamic>{
- 'FlutterRepositoryPath': 'flutter/flutter',
- 'CreateTimestamp': 1,
- 'Commit': <String, dynamic>{
- 'Sha': 'd5b0b3c8d1c5fd89302089077ccabbcfaae045e4',
- 'Message': 'test message 2',
- 'Author': <String, dynamic>{'Login': null, 'avatar_url': null},
- },
- 'Branch': 'master',
- },
- },
- 'Stages': <String>[],
- });
- });
-
- test('reports statuses with input branch', () async {
- commit2.branch = 'flutter-1.1-candidate.1';
- config.db.values[commit1.key] = commit1;
- config.db.values[commit2.key] = commit2;
- buildStatusService = FakeBuildStatusService(
- commitStatuses: <CommitStatus>[CommitStatus(commit1, const <Stage>[]), CommitStatus(commit2, const <Stage>[])],
- );
- handler = GetStatus(
- config: config,
- datastoreProvider: (DatastoreDB db) => DatastoreService(config.db, 5),
- buildStatusProvider: (_) => buildStatusService,
- );
-
- const String branch = 'flutter-1.1-candidate.1';
-
- expect(config.db.values.length, 2);
-
- tester.request = FakeHttpRequest(
- queryParametersValue: <String, String>{
- GetStatus.kBranchParam: branch,
- },
- );
- final Map<String, dynamic> result = (await decodeHandlerBody())!;
-
- expect(result['Statuses'].length, 1);
- expect(result['Statuses'].first, <String, dynamic>{
- 'Checklist': <String, dynamic>{
- 'Key':
- 'ahFmbHV0dGVyLWRhc2hib2FyZHJHCxIJQ2hlY2tsaXN0IjhmbHV0dGVyL2ZsdXR0ZXIvZDViMGIzYzhkMWM1ZmQ4OTMwMjA4OTA3N2NjYWJiY2ZhYWUwNDVlNAyiAQlbZGVmYXVsdF0',
- 'Checklist': <String, dynamic>{
- 'FlutterRepositoryPath': 'flutter/flutter',
- 'CreateTimestamp': 1,
- 'Commit': <String, dynamic>{
- 'Sha': 'd5b0b3c8d1c5fd89302089077ccabbcfaae045e4',
- 'Message': 'test message 2',
- 'Author': <String, dynamic>{'Login': null, 'avatar_url': null},
- },
- 'Branch': 'flutter-1.1-candidate.1',
- },
- },
- 'Stages': <String>[],
- });
- });
- });
-}
diff --git a/app_dart/test/request_handlers/github/webhook_subscription_test.dart b/app_dart/test/request_handlers/github/webhook_subscription_test.dart
deleted file mode 100644
index d54f76f..0000000
--- a/app_dart/test/request_handlers/github/webhook_subscription_test.dart
+++ /dev/null
@@ -1,2322 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:cocoon_service/src/model/appengine/commit.dart';
-import 'package:cocoon_service/src/model/luci/buildbucket.dart';
-import 'package:cocoon_service/src/request_handlers/github/webhook_subscription.dart';
-import 'package:cocoon_service/src/service/cache_service.dart';
-import 'package:cocoon_service/src/service/config.dart';
-import 'package:cocoon_service/src/service/datastore.dart';
-
-import 'package:github/github.dart' hide Branch;
-import 'package:googleapis/bigquery/v2.dart';
-import 'package:mockito/mockito.dart';
-import 'package:test/test.dart';
-
-import '../../src/datastore/fake_config.dart';
-import '../../src/datastore/fake_datastore.dart';
-import '../../src/request_handling/fake_http.dart';
-import '../../src/request_handling/subscription_tester.dart';
-import '../../src/service/fake_buildbucket.dart';
-import '../../src/service/fake_github_service.dart';
-import '../../src/service/fake_gerrit_service.dart';
-import '../../src/service/fake_scheduler.dart';
-import '../../src/utilities/entity_generators.dart';
-import '../../src/utilities/mocks.dart';
-import '../../src/utilities/webhook_generators.dart';
-
-void main() {
- late GithubWebhookSubscription webhook;
- late FakeBuildBucketClient fakeBuildBucketClient;
- late FakeConfig config;
- late FakeDatastoreDB db;
- late FakeGithubService githubService;
- late FakeHttpRequest request;
- late FakeScheduler scheduler;
- late FakeGerritService gerritService;
- late MockCommitService commitService;
- late MockGitHub gitHubClient;
- late MockGithubChecksUtil mockGithubChecksUtil;
- late MockGithubChecksService mockGithubChecksService;
- late MockIssuesService issuesService;
- late MockPullRequestsService pullRequestsService;
- late SubscriptionTester tester;
-
- /// Name of an example release base branch name.
- const String kReleaseBaseRef = 'flutter-2.12-candidate.4';
-
- /// Name of an example release head branch name.
- const String kReleaseHeadRef = 'cherrypicks-flutter-2.12-candidate.4';
-
- setUp(() {
- request = FakeHttpRequest();
- db = FakeDatastoreDB();
- gitHubClient = MockGitHub();
- githubService = FakeGithubService();
- commitService = MockCommitService();
- final MockTabledataResource tabledataResource = MockTabledataResource();
- when(tabledataResource.insertAll(any, any, any, any)).thenAnswer((_) async => TableDataInsertAllResponse());
- config = FakeConfig(
- dbValue: db,
- githubClient: gitHubClient,
- githubService: githubService,
- githubOAuthTokenValue: 'githubOAuthKey',
- missingTestsPullRequestMessageValue: 'missingTestPullRequestMessage',
- releaseBranchPullRequestMessageValue: 'releaseBranchPullRequestMessage',
- rollerAccountsValue: const <String>{
- 'skia-flutter-autoroll',
- 'engine-flutter-autoroll',
- 'dependabot',
- 'dependabot[bot]',
- },
- tabledataResource: tabledataResource,
- wrongHeadBranchPullRequestMessageValue: 'wrongHeadBranchPullRequestMessage',
- wrongBaseBranchPullRequestMessageValue: '{{target_branch}} -> {{default_branch}}',
- );
- issuesService = MockIssuesService();
- when(issuesService.addLabelsToIssue(any, any, any)).thenAnswer((_) async => <IssueLabel>[]);
- when(issuesService.createComment(any, any, any)).thenAnswer((_) async => IssueComment());
- when(issuesService.listCommentsByIssue(any, any))
- .thenAnswer((_) => Stream<IssueComment>.fromIterable(<IssueComment>[IssueComment()]));
- pullRequestsService = MockPullRequestsService();
- when(pullRequestsService.listFiles(Config.flutterSlug, any))
- .thenAnswer((_) => const Stream<PullRequestFile>.empty());
- when(pullRequestsService.edit(any, any, title: anyNamed('title'), state: anyNamed('state'), base: anyNamed('base')))
- .thenAnswer((_) async => PullRequest());
- fakeBuildBucketClient = FakeBuildBucketClient();
- mockGithubChecksUtil = MockGithubChecksUtil();
- scheduler = FakeScheduler(
- config: config,
- buildbucket: fakeBuildBucketClient,
- githubChecksUtil: mockGithubChecksUtil,
- );
- tester = SubscriptionTester(request: request);
-
- mockGithubChecksService = MockGithubChecksService();
- when(gitHubClient.issues).thenReturn(issuesService);
- when(gitHubClient.pullRequests).thenReturn(pullRequestsService);
- when(mockGithubChecksUtil.createCheckRun(any, any, any, any, output: anyNamed('output'))).thenAnswer((_) async {
- return CheckRun.fromJson(const <String, dynamic>{
- 'id': 1,
- 'started_at': '2020-05-10T02:49:31Z',
- 'check_suite': <String, dynamic>{'id': 2},
- });
- });
-
- gerritService = FakeGerritService();
- webhook = GithubWebhookSubscription(
- config: config,
- cache: CacheService(inMemory: true),
- datastoreProvider: (_) => DatastoreService(config.db, 5),
- gerritService: gerritService,
- githubChecksService: mockGithubChecksService,
- scheduler: scheduler,
- commitService: commitService,
- );
- });
-
- group('github webhook pull_request event', () {
- test('Closes PR opened from dev', () async {
- const int issueNumber = 123;
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- headRef: 'dev',
- );
- when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.value(
- PullRequestFile()..filename = 'packages/flutter/blah.dart',
- ),
- );
-
- when(issuesService.listCommentsByIssue(Config.flutterSlug, issueNumber)).thenAnswer(
- (_) => Stream<IssueComment>.value(
- IssueComment()..body = 'some other comment',
- ),
- );
-
- await tester.post(webhook);
-
- verify(
- pullRequestsService.edit(
- Config.flutterSlug,
- issueNumber,
- state: 'closed',
- ),
- ).called(1);
-
- verify(
- issuesService.createComment(
- Config.flutterSlug,
- issueNumber,
- argThat(contains(config.wrongHeadBranchPullRequestMessageValue)),
- ),
- ).called(1);
- });
-
- test('No action against candidate branches', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- baseRef: 'flutter-2.13-candidate.0',
- );
-
- when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.value(
- PullRequestFile()..filename = 'packages/flutter/blah.dart',
- ),
- );
-
- when(issuesService.listCommentsByIssue(Config.flutterSlug, issueNumber)).thenAnswer(
- (_) => Stream<IssueComment>.value(
- IssueComment()..body = 'some other comment',
- ),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- pullRequestsService.edit(
- Config.flutterSlug,
- issueNumber,
- base: kDefaultBranchName,
- ),
- );
-
- verifyNever(
- issuesService.createComment(
- Config.flutterSlug,
- issueNumber,
- argThat(contains('-> master')),
- ),
- );
- });
-
- test('Acts on opened against dev', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- baseRef: 'dev',
- );
-
- when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.value(
- PullRequestFile()..filename = 'packages/flutter/blah.dart',
- ),
- );
-
- when(issuesService.listCommentsByIssue(Config.flutterSlug, issueNumber)).thenAnswer(
- (_) => Stream<IssueComment>.value(
- IssueComment()..body = 'some other comment',
- ),
- );
-
- await tester.post(webhook);
-
- verify(
- pullRequestsService.edit(
- Config.flutterSlug,
- issueNumber,
- base: kDefaultBranchName,
- ),
- ).called(1);
-
- verify(
- issuesService.createComment(
- Config.flutterSlug,
- issueNumber,
- argThat(contains('dev -> master')),
- ),
- ).called(1);
- });
-
- test('Acts on closed, cancels presubmit targets', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'closed',
- number: issueNumber,
- baseRef: 'dev',
- merged: false,
- );
-
- await tester.post(webhook);
-
- expect(scheduler.cancelPreSubmitTargetsCallCnt, 1);
- expect(scheduler.addPullRequestCallCnt, 0);
- });
-
- test('Acts on closed, cancels presubmit targets, add pr for postsubmit target create', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'closed',
- number: issueNumber,
- baseRef: 'dev',
- merged: true,
- baseSha: 'sha1',
- mergeCommitSha: 'sha2',
- );
-
- await tester.post(webhook);
-
- expect(scheduler.cancelPreSubmitTargetsCallCnt, 1);
- expect(scheduler.addPullRequestCallCnt, 1);
- });
-
- test('Acts on opened against master when default is main', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- baseRef: 'master',
- slug: Config.engineSlug,
- );
-
- when(pullRequestsService.listFiles(Config.engineSlug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.value(
- PullRequestFile()..filename = 'packages/flutter/blah.dart',
- ),
- );
-
- when(issuesService.listCommentsByIssue(Config.engineSlug, issueNumber)).thenAnswer(
- (_) => Stream<IssueComment>.value(
- IssueComment()..body = 'some other comment',
- ),
- );
-
- await tester.post(webhook);
-
- verify(
- pullRequestsService.edit(
- Config.engineSlug,
- issueNumber,
- base: 'main',
- ),
- ).called(1);
-
- verify(
- issuesService.createComment(
- Config.engineSlug,
- issueNumber,
- argThat(contains('master -> main')),
- ),
- ).called(1);
- });
-
- // We already schedule checks when a draft is opened, don't need to re-test
- // just because it was marked ready for review
- test('Does nothing on ready_for_review', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'ready_for_review',
- number: issueNumber,
- );
- bool batchRequestCalled = false;
-
- Future<BatchResponse> getBatchResponse() async {
- batchRequestCalled = true;
- fail('Marking a draft ready for review should not trigger new builds');
- }
-
- fakeBuildBucketClient.batchResponse = getBatchResponse;
-
- await tester.post(webhook);
-
- expect(batchRequestCalled, isFalse);
- });
-
- test('Triggers builds when opening a draft PR', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- isDraft: true,
- );
- bool batchRequestCalled = false;
-
- Future<BatchResponse> getBatchResponse() async {
- batchRequestCalled = true;
- return BatchResponse(
- responses: <Response>[
- Response(
- searchBuilds: SearchBuildsResponse(
- builds: <Build>[
- generateBuild(999, name: 'Linux', status: Status.ended),
- ],
- ),
- ),
- Response(
- searchBuilds: SearchBuildsResponse(
- builds: <Build>[
- generateBuild(998, name: 'Linux', status: Status.ended),
- ],
- ),
- ),
- ],
- );
- }
-
- fakeBuildBucketClient.batchResponse = getBatchResponse;
-
- await tester.post(webhook);
-
- expect(batchRequestCalled, isTrue);
- expect(scheduler.cancelPreSubmitTargetsCallCnt, 1);
- });
-
- test('Does nothing against cherry pick PR', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- baseRef: 'flutter-1.20-candidate.7',
- headRef: 'cherrypicks-flutter-1.20-candidate.7',
- );
- when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.value(
- PullRequestFile()..filename = 'packages/flutter/blah.dart',
- ),
- );
-
- when(issuesService.listCommentsByIssue(Config.flutterSlug, issueNumber)).thenAnswer(
- (_) => Stream<IssueComment>.value(
- IssueComment()..body = 'some other comment',
- ),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- pullRequestsService.edit(
- Config.flutterSlug,
- issueNumber,
- base: kDefaultBranchName,
- ),
- );
-
- verifyNever(
- issuesService.createComment(
- Config.flutterSlug,
- issueNumber,
- argThat(contains(config.wrongBaseBranchPullRequestMessage)),
- ),
- );
- });
-
- test('release PRs are approved', () async {
- const int issueNumber = 123;
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- // Base is where the PR will merge into
- baseRef: 'flutter-2.13-candidate.0',
- login: 'dart-flutter-releaser',
- );
- when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber))
- .thenAnswer((_) => const Stream<PullRequestFile>.empty());
- when(pullRequestsService.createReview(Config.flutterSlug, any))
- .thenAnswer((_) async => PullRequestReview(id: 123, user: User()));
-
- await tester.post(webhook);
-
- final List<dynamic> reviews = verify(pullRequestsService.createReview(Config.flutterSlug, captureAny)).captured;
- expect(reviews.length, 1);
- final CreatePullRequestReview review = reviews.single as CreatePullRequestReview;
- expect(review.event, 'APPROVE');
- });
-
- test('fake release PRs are not approved', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- // Base is where the PR will merge into
- baseRef: 'master',
- // Head is the branch from the fork
- headRef: 'flutter-2.13-candidate.0',
- login: 'dart-flutter-releaser',
- );
- when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber))
- .thenAnswer((_) => const Stream<PullRequestFile>.empty());
- when(pullRequestsService.createReview(Config.flutterSlug, any))
- .thenAnswer((_) async => PullRequestReview(id: 123, user: User()));
-
- await tester.post(webhook);
-
- verifyNever(pullRequestsService.createReview(Config.flutterSlug, captureAny));
- });
-
- test('release PRs are not approved for outsider PRs', () async {
- const int issueNumber = 123;
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- headRef: 'flutter-2.13-candidate.0',
- );
- when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber))
- .thenAnswer((_) => const Stream<PullRequestFile>.empty());
- when(pullRequestsService.createReview(Config.flutterSlug, any))
- .thenAnswer((_) async => PullRequestReview(id: 123, user: User()));
-
- await tester.post(webhook);
-
- verifyNever(pullRequestsService.createReview(Config.flutterSlug, any));
- });
-
- test('Framework labels PRs, comment if no tests', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- );
- when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.value(
- PullRequestFile()..filename = 'packages/flutter/blah.dart',
- ),
- );
-
- when(issuesService.listCommentsByIssue(Config.flutterSlug, issueNumber)).thenAnswer(
- (_) => Stream<IssueComment>.value(
- IssueComment()..body = 'some other comment',
- ),
- );
-
- await tester.post(webhook);
-
- verify(
- issuesService.createComment(
- Config.flutterSlug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- ).called(1);
- });
-
- group('Auto-roller accounts do not label Framework PR with test label or comment.', () {
- final Set<String> inputs = {
- 'skia-flutter-autoroll',
- 'dependabot',
- };
-
- for (String element in inputs) {
- test('Framework does not label PR with no tests label if author is $element', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- login: element,
- );
-
- final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
- when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.value(
- PullRequestFile()..filename = 'packages/flutter/blah.dart',
- ),
- );
-
- when(issuesService.listCommentsByIssue(slug, issueNumber)).thenAnswer(
- (_) => Stream<IssueComment>.value(
- IssueComment()..body = 'some other comment',
- ),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.addLabelsToIssue(
- slug,
- issueNumber,
- any,
- ),
- );
-
- verifyNever(
- issuesService.createComment(
- slug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
- }
- });
-
- test('Framework does not label PR with no tests label if author is engine-flutter-autoroll', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- login: 'engine-flutter-autoroll',
- );
- final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
- when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.value(
- PullRequestFile()..filename = 'packages/flutter/blah.dart',
- ),
- );
-
- when(issuesService.listCommentsByIssue(slug, issueNumber)).thenAnswer(
- (_) => Stream<IssueComment>.value(
- IssueComment()..body = 'some other comment',
- ),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.addLabelsToIssue(
- slug,
- issueNumber,
- any,
- ),
- );
-
- verifyNever(
- issuesService.createComment(
- slug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
-
- test('Framework does not label PR with no tests label if file is test exempt', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- );
- final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
- when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
- PullRequestFile()..filename = 'dev/devicelab/lib/versions/gallery.dart',
- PullRequestFile()..filename = 'dev/integration_tests/some_package/android/build.gradle',
- PullRequestFile()..filename = 'impeller/fixtures/dart_tests.dart',
- PullRequestFile()..filename = 'impeller/golden_tests/golden_tests.cc',
- PullRequestFile()..filename = 'impeller/playground/playground.cc',
- PullRequestFile()..filename = 'shell/platform/embedder/tests/embedder_test_context.cc',
- PullRequestFile()..filename = 'shell/platform/embedder/fixtures/main.dart',
- ]),
- );
-
- when(issuesService.listCommentsByIssue(slug, issueNumber)).thenAnswer(
- (_) => Stream<IssueComment>.value(
- IssueComment()..body = 'some other comment',
- ),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.addLabelsToIssue(
- slug,
- issueNumber,
- any,
- ),
- );
-
- verifyNever(
- issuesService.createComment(
- slug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
-
- test('Framework labels PRs, comment if no tests including hit_test.dart file', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- );
- final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
- when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.value(
- PullRequestFile()
- ..additionsCount = 10
- ..changesCount = 10
- ..filename = 'packages/flutter/lib/src/gestures/hit_test.dart',
- ),
- );
-
- when(issuesService.listCommentsByIssue(slug, issueNumber)).thenAnswer(
- (_) => Stream<IssueComment>.value(
- IssueComment()..body = 'some other comment',
- ),
- );
-
- await tester.post(webhook);
-
- verify(
- issuesService.createComment(
- slug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- ).called(1);
- });
-
- test('Framework labels PRs, no dart files', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- );
- final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
- when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.value(
- PullRequestFile()..filename = 'packages/flutter/blah.md',
- ),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.addLabelsToIssue(
- slug,
- issueNumber,
- any,
- ),
- );
-
- verifyNever(
- issuesService.createComment(
- slug,
- issueNumber,
- any,
- ),
- );
- });
-
- test('Framework labels PRs, no comment if tests', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- );
- final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
- when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
- PullRequestFile()..filename = 'packages/flutter/semantics_test.dart',
- PullRequestFile()..filename = 'packages/flutter_tools/blah.dart',
- PullRequestFile()..filename = 'packages/flutter_driver/blah.dart',
- PullRequestFile()..filename = 'examples/flutter_gallery/blah.dart',
- PullRequestFile()..filename = 'dev/bots/test.dart',
- PullRequestFile()..filename = 'dev/devicelab/bin/tasks/analyzer_benchmark.dart',
- PullRequestFile()..filename = 'bin/internal/engine.version',
- PullRequestFile()..filename = 'packages/flutter/lib/src/cupertino/blah.dart',
- PullRequestFile()..filename = 'packages/flutter/lib/src/material/blah.dart',
- PullRequestFile()..filename = 'packages/flutter_localizations/blah.dart',
- ]),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.createComment(
- slug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
-
- test('Framework labels dart fix PRs, no comment if tests', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- );
- final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
- when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
- PullRequestFile()..filename = 'packages/flutter/test_fixes/material.dart',
- PullRequestFile()..filename = 'packages/flutter/test_fixes/material.expect',
- ]),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.createComment(
- slug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
-
- test('Framework labels bot PR, no comment', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- login: 'fluttergithubbot',
- );
- final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
- when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
- PullRequestFile()..filename = 'packages/flutter_tools/blah.dart',
- PullRequestFile()..filename = 'packages/flutter_driver/blah.dart',
- ]),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.createComment(
- slug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
-
- test('Framework labels deletion only PR, no test request', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- );
- final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
- when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
- PullRequestFile()
- ..filename = 'packages/flutter/blah.dart'
- ..deletionsCount = 20
- ..additionsCount = 0
- ..changesCount = 20,
- ]),
- );
-
- await tester.post(webhook);
-
- // The PR here is only deleting code, so no test comment.
- verifyNever(
- issuesService.createComment(
- slug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
-
- test('PR with additions and deletions is commented and labeled', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- );
- final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
- when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
- PullRequestFile()
- ..filename = 'packages/flutter/blah.dart'
- ..deletionsCount = 20
- ..additionsCount = 1
- ..changesCount = 21,
- ]),
- );
-
- await tester.post(webhook);
-
- verify(
- issuesService.createComment(
- slug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- ).called(1);
- });
-
- test('Framework no comment if code has only devicelab test', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- );
- final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
- when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
- PullRequestFile()..filename = 'packages/flutter_tools/lib/src/ios/devices.dart',
- PullRequestFile()..filename = 'dev/devicelab/lib/tasks/plugin_tests.dart',
- ]),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.createComment(
- slug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
-
- test('Framework no comment if only dev bots or devicelab changed', () async {
- const int issueNumber = 123;
- tester.message = generateGithubWebhookMessage(action: 'opened', number: issueNumber);
-
- when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
- PullRequestFile()..filename = 'dev/bots/test.dart',
- PullRequestFile()..filename = 'dev/devicelab/bin/tasks/analyzer_benchmark.dart',
- PullRequestFile()..filename = 'dev/devicelab/lib/tasks/plugin_tests.dart',
- PullRequestFile()..filename = 'dev/benchmarks/microbenchmarks/lib/foundation/all_elements_bench.dart',
- ]),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.createComment(
- Config.flutterSlug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
-
- test('Framework no comment if only .gitignore changed', () async {
- const int issueNumber = 123;
- tester.message = generateGithubWebhookMessage(action: 'opened', number: issueNumber);
-
- when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
- PullRequestFile()..filename = '.gitignore',
- PullRequestFile()..filename = 'dev/integration_tests/foo_app/.gitignore',
- ]),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.createComment(
- any,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
-
- test('Framework no test comment if Objective-C test changed', () async {
- const int issueNumber = 123;
- tester.message = generateGithubWebhookMessage(action: 'opened', number: issueNumber);
-
- when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
- // Example of real behavior code change.
- PullRequestFile()
- ..filename = 'packages/flutter_tools/templates/app_shared/macos.tmpl/Runner/Base.lproj/MainMenu.xib',
- // Example of Objective-C test.
- PullRequestFile()..filename = 'dev/integration_tests/flutter_gallery/macos/RunnerTests/RunnerTests.m',
- ]),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.createComment(
- Config.flutterSlug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
-
- test('Framework no comment if only AUTHORS changed', () async {
- const int issueNumber = 123;
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- );
-
- when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
- PullRequestFile()..filename = 'AUTHORS',
- ]),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.createComment(
- Config.flutterSlug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
-
- test('Framework no comment if only ci.yamlchanged', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- );
- final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
- when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
- PullRequestFile()..filename = '.ci.yaml',
- ]),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.createComment(
- slug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
-
- test('Framework no comment if only analysis options changed', () async {
- const int issueNumber = 123;
- tester.message = generateGithubWebhookMessage(action: 'opened', number: issueNumber);
- final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
- when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
- PullRequestFile()..filename = 'analysis_options.yaml',
- ]),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.createComment(
- slug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
-
- test('Framework no comment if only CODEOWNERS or TESTOWNERS changed', () async {
- const int issueNumber = 123;
- tester.message = generateGithubWebhookMessage(action: 'opened', number: issueNumber);
- final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
- when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
- PullRequestFile()..filename = 'CODEOWNERS',
- PullRequestFile()..filename = 'TESTOWNERS',
- ]),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.createComment(
- slug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
-
- for (String extention in knownCommentCodeExtensions) {
- test('Framework no comment if only comments changed .$extention', () async {
- const int issueNumber = 123;
- tester.message = generateGithubWebhookMessage(action: 'opened', number: issueNumber);
- final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
- const String patch = '''
-@@ -128,7 +128,7 @@
-
-/// Insert interesting comment here.
-///
--/// More details here, but some of them are wrong.
-+/// These are the right details!
-void foo() {
- int bar = 0;
- String baz = '';
-''';
-
- when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
- PullRequestFile()
- ..filename = 'packages/foo/lib/foo.$extention'
- ..additionsCount = 1
- ..deletionsCount = 1
- ..changesCount = 2
- ..patch = patch,
- ]),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.createComment(
- slug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
- }
-
- test('Framework labels PRs, no comment if tests (dev/bots/test.dart)', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- );
-
- when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
- PullRequestFile()..filename = 'dev/bots/test.dart',
- PullRequestFile()..filename = 'packages/flutter_tools/blah.dart',
- PullRequestFile()..filename = 'packages/flutter_driver/blah.dart',
- PullRequestFile()..filename = 'examples/flutter_gallery/blah.dart',
- PullRequestFile()..filename = 'packages/flutter/lib/src/material/blah.dart',
- PullRequestFile()..filename = 'packages/flutter_localizations/blah.dart',
- ]),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.createComment(
- Config.flutterSlug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
-
- test('Framework labels PRs, no comment if tests (dev/bots/analyze.dart)', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- );
- final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
- when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
- PullRequestFile()..filename = 'dev/bots/analyze.dart',
- PullRequestFile()..filename = 'packages/flutter_tools/blah.dart',
- PullRequestFile()..filename = 'packages/flutter_driver/blah.dart',
- PullRequestFile()..filename = 'examples/flutter_gallery/blah.dart',
- PullRequestFile()..filename = 'packages/flutter/lib/src/material/blah.dart',
- PullRequestFile()..filename = 'packages/flutter_localizations/blah.dart',
- ]),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.createComment(
- slug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
-
- test('Framework labels PRs, no comment if tests (flutter_tools/test/helper.dart)', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- );
-
- when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
- PullRequestFile()..filename = 'packages/flutter_tools/blah.dart',
- PullRequestFile()..filename = 'packages/flutter_tools/test/helper.dart',
- ]),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.createComment(
- Config.flutterSlug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
-
- test('Framework labels PRs, apply label but no comment when rolling engine version', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- baseRef: kReleaseBaseRef,
- headRef: kReleaseHeadRef,
- );
-
- when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
- PullRequestFile()
- ..filename = 'bin/internal/engine.version'
- ..deletionsCount = 20
- ..additionsCount = 1
- ..changesCount = 21,
- ]),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.addLabelsToIssue(
- Config.flutterSlug,
- issueNumber,
- any,
- ),
- );
-
- verifyNever(
- issuesService.createComment(
- Config.flutterSlug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
-
- test('Engine labels PRs, comment if no tests', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- slug: Config.engineSlug,
- baseRef: Config.defaultBranch(Config.engineSlug),
- );
- final RepositorySlug slug = RepositorySlug('flutter', 'engine');
-
- when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.value(
- PullRequestFile()..filename = 'shell/platform/darwin/ios/framework/Source/boost.mm',
- ),
- );
-
- when(issuesService.listCommentsByIssue(slug, issueNumber)).thenAnswer(
- (_) => Stream<IssueComment>.value(
- IssueComment()..body = 'some other comment',
- ),
- );
-
- await tester.post(webhook);
-
- verify(
- issuesService.createComment(
- slug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- ).called(1);
- });
-
- group('Auto-roller accounts do not label Engine PR with test label or comment.', () {
- final Set<String> inputs = {
- 'engine-flutter-autoroll',
- 'dependabot',
- 'dependabot[bot]',
- };
-
- for (String element in inputs) {
- test('Engine does not label PR for no tests if author is $element', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- slug: Config.engineSlug,
- login: element,
- );
-
- when(pullRequestsService.listFiles(Config.engineSlug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.value(
- PullRequestFile()..filename = 'shell/platform/darwin/ios/framework/Source/boost.mm',
- ),
- );
-
- when(issuesService.listCommentsByIssue(Config.engineSlug, issueNumber)).thenAnswer(
- (_) => Stream<IssueComment>.value(
- IssueComment()..body = 'some other comment',
- ),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.createComment(
- Config.engineSlug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
- }
- });
-
- test('Engine does not label PR for no tests if on branch', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- slug: Config.engineSlug,
- baseRef: 'flutter-3.12-candidate.1',
- );
-
- when(pullRequestsService.listFiles(Config.engineSlug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.value(
- PullRequestFile()..filename = 'shell/platform/darwin/ios/framework/Source/boost.mm',
- ),
- );
-
- when(issuesService.listCommentsByIssue(Config.engineSlug, issueNumber)).thenAnswer(
- (_) => Stream<IssueComment>.value(
- IssueComment()..body = 'some other comment',
- ),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.createComment(
- Config.engineSlug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
-
- test('Engine does not label PR for no tests if author is skia-flutter-autoroll', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- slug: Config.engineSlug,
- login: 'skia-flutter-autoroll',
- );
-
- when(pullRequestsService.listFiles(Config.engineSlug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.value(
- PullRequestFile()..filename = 'shell/platform/darwin/ios/framework/Source/boost.mm',
- ),
- );
-
- when(issuesService.listCommentsByIssue(Config.engineSlug, issueNumber)).thenAnswer(
- (_) => Stream<IssueComment>.value(
- IssueComment()..body = 'some other comment',
- ),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.createComment(
- Config.engineSlug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
-
- test('Engine labels PRs, no code files', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- baseRef: 'main',
- slug: Config.engineSlug,
- );
-
- when(pullRequestsService.listFiles(Config.engineSlug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.value(
- PullRequestFile()..filename = 'DEPS',
- ),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.addLabelsToIssue(
- Config.engineSlug,
- issueNumber,
- any,
- ),
- );
-
- verifyNever(
- issuesService.createComment(
- Config.engineSlug,
- issueNumber,
- any,
- ),
- );
- });
-
- test('Engine labels PRs, no comment if Java tests', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- slug: Config.engineSlug,
- );
-
- when(pullRequestsService.listFiles(Config.engineSlug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
- PullRequestFile()..filename = 'shell/platform/android/io/flutter/Blah.java',
- PullRequestFile()..filename = 'shell/platform/android/test/io/flutter/BlahTest.java',
- ]),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.addLabelsToIssue(Config.engineSlug, issueNumber, any),
- );
-
- verifyNever(
- issuesService.createComment(
- Config.engineSlug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
-
- test('Engine labels PRs, no comment if script tests', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- slug: Config.engineSlug,
- );
-
- when(pullRequestsService.listFiles(Config.engineSlug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
- PullRequestFile()..filename = 'fml/blah.cc',
- PullRequestFile()..filename = 'fml/testing/blah_test.sh',
- ]),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.createComment(
- Config.engineSlug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
-
- test('Engine labels PRs, no comment if cc tests', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- slug: Config.engineSlug,
- );
-
- when(pullRequestsService.listFiles(Config.engineSlug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
- PullRequestFile()..filename = 'fml/blah.cc',
- PullRequestFile()..filename = 'fml/blah_unittests.cc',
- ]),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.addLabelsToIssue(
- Config.engineSlug,
- issueNumber,
- any,
- ),
- );
-
- verifyNever(
- issuesService.createComment(
- Config.engineSlug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
-
- test('Engine labels PRs, no comment if py tests', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- slug: Config.engineSlug,
- );
-
- when(pullRequestsService.listFiles(Config.engineSlug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
- PullRequestFile()..filename = 'tools/font-subset/main.cc',
- PullRequestFile()..filename = 'tools/font-subset/test.py',
- ]),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.addLabelsToIssue(
- Config.engineSlug,
- issueNumber,
- any,
- ),
- );
-
- verifyNever(
- issuesService.createComment(
- Config.engineSlug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
-
- test('Engine labels PRs, no comment if cc benchmarks', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- slug: Config.engineSlug,
- );
-
- when(pullRequestsService.listFiles(Config.engineSlug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
- PullRequestFile()..filename = 'fml/blah.cc',
- PullRequestFile()..filename = 'fml/blah_benchmarks.cc',
- ]),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.addLabelsToIssue(
- Config.engineSlug,
- issueNumber,
- any,
- ),
- );
-
- verifyNever(
- issuesService.createComment(
- Config.engineSlug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
-
- test('Engine labels PRs, no comments if pr is for release branches', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- baseRef: kReleaseBaseRef,
- headRef: kReleaseHeadRef,
- slug: Config.engineSlug,
- );
- when(pullRequestsService.listFiles(Config.engineSlug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.value(
- PullRequestFile()..filename = 'shell/platform/darwin/ios/framework/Source/boost.mm',
- ),
- );
-
- when(issuesService.listCommentsByIssue(Config.engineSlug, issueNumber)).thenAnswer(
- (_) => Stream<IssueComment>.value(
- IssueComment()..body = 'some other comment',
- ),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.createComment(
- Config.engineSlug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
-
- test('bot does not comment for whitespace only changes', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- slug: Config.engineSlug,
- );
- const String patch = '''
-@@ -128,7 +128,7 @@
-
- int bar = 0;
-+
- int baz = 0;
-''';
-
- when(pullRequestsService.listFiles(Config.engineSlug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
- PullRequestFile()
- ..filename = 'flutter/lib/ui/foo.dart'
- ..additionsCount = 1
- ..deletionsCount = 1
- ..changesCount = 2
- ..patch = patch,
- ]),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.createComment(
- Config.engineSlug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
-
- test('Engine does not comment for comment-only changes', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- slug: Config.engineSlug,
- );
- const String patch = '''
-@@ -128,7 +128,7 @@
-
-/// Insert interesting comment here.
-///
--/// More details here, but some of them are wrong.
-+/// These are the right details!
-void foo() {
- int bar = 0;
- String baz = '';
-''';
-
- when(pullRequestsService.listFiles(Config.engineSlug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
- PullRequestFile()
- ..filename = 'flutter/lib/ui/foo.dart'
- ..additionsCount = 1
- ..deletionsCount = 1
- ..changesCount = 2
- ..patch = patch,
- ]),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.createComment(
- Config.engineSlug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
-
- test('Engine labels deletion only PR, no test request', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- slug: Config.engineSlug,
- );
-
- when(pullRequestsService.listFiles(Config.engineSlug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
- PullRequestFile()
- ..filename = 'flutter/lib/ui/foo.dart'
- ..deletionsCount = 20
- ..additionsCount = 0
- ..changesCount = 20,
- ]),
- );
-
- await tester.post(webhook);
-
- // The PR here is only deleting code, so no test comment.
- verifyNever(
- issuesService.createComment(
- Config.engineSlug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
-
- test('No labels when only pubspec.yaml changes', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- );
-
- when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
- PullRequestFile()..filename = 'packages/flutter/pubspec.yaml',
- PullRequestFile()..filename = 'packages/flutter_tools/pubspec.yaml',
- ]),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.createComment(
- Config.flutterSlug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
-
- test('Packages does not comment if Pigeon native tests', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- slug: Config.packagesSlug,
- baseRef: Config.defaultBranch(Config.packagesSlug),
- );
- when(pullRequestsService.listFiles(Config.packagesSlug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
- PullRequestFile()..filename = 'packages/pigeon/lib/swift_generator.dart',
- PullRequestFile()
- ..filename = 'packages/pigeon/platform_tests/shared_test_plugin_code/lib/integration_tests.dart',
- ]),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.createComment(
- Config.packagesSlug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
-
- test('Packages does not comment if editing test files in go_router', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- slug: Config.packagesSlug,
- baseRef: Config.defaultBranch(Config.packagesSlug),
- );
- when(pullRequestsService.listFiles(Config.packagesSlug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
- PullRequestFile()
- ..filename = 'packages/packages/go_router/test_fixes/go_router.dart'
- ..additionsCount = 10,
- PullRequestFile()
- ..filename = 'packages/packages/go_router/lib/fix_data.yaml'
- ..additionsCount = 10,
- ]),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.createComment(
- Config.packagesSlug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
-
- test('Packages does not comment if editing test files in go_router_builder', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- slug: Config.packagesSlug,
- baseRef: Config.defaultBranch(Config.packagesSlug),
- );
- when(pullRequestsService.listFiles(Config.packagesSlug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
- PullRequestFile()
- ..filename = 'packages/packages/go_router_builder/lib/src/route_config.dart'
- ..additionsCount = 10,
- PullRequestFile()
- ..filename = 'packages/packages/go_router_builder/test_inputs/bad_path_pattern.dart'
- ..additionsCount = 10,
- ]),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.createComment(
- Config.packagesSlug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
-
- test('Packages comments and labels if no tests', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- slug: Config.packagesSlug,
- baseRef: Config.defaultBranch(Config.packagesSlug),
- );
- when(pullRequestsService.listFiles(Config.packagesSlug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.value(
- PullRequestFile()..filename = 'packages/foo/lib/foo.dart',
- ),
- );
-
- when(issuesService.listCommentsByIssue(Config.packagesSlug, issueNumber)).thenAnswer(
- (_) => Stream<IssueComment>.value(
- IssueComment()..body = 'some other comment',
- ),
- );
-
- await tester.post(webhook);
-
- verify(
- issuesService.createComment(
- Config.packagesSlug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- ).called(1);
- });
-
- test('Packages do not comment or label if pr is for release branches', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- baseRef: kReleaseBaseRef,
- headRef: kReleaseHeadRef,
- slug: Config.packagesSlug,
- );
-
- when(pullRequestsService.listFiles(Config.packagesSlug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.value(
- PullRequestFile()..filename = 'packages/foo/lib/foo.dart',
- ),
- );
-
- when(issuesService.listCommentsByIssue(Config.packagesSlug, issueNumber)).thenAnswer(
- (_) => Stream<IssueComment>.value(
- IssueComment()..body = 'some other comment',
- ),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.createComment(
- Config.packagesSlug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
-
- verifyNever(
- issuesService.addLabelsToIssue(
- Config.packagesSlug,
- issueNumber,
- any,
- ),
- );
- });
-
- test('Packages does not comment if Dart tests', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- slug: Config.packagesSlug,
- );
-
- when(pullRequestsService.listFiles(Config.packagesSlug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
- PullRequestFile()..filename = 'packages/foo/lib/foo.dart',
- PullRequestFile()..filename = 'packages/foo/test/foo_test.dart',
- ]),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.createComment(
- Config.packagesSlug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
-
- test('Packages does not comment for custom test driver', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- slug: Config.packagesSlug,
- );
-
- when(pullRequestsService.listFiles(Config.packagesSlug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.fromIterable(<PullRequestFile>[
- PullRequestFile()..filename = 'packages/foo/tool/run_tests.dart',
- PullRequestFile()..filename = 'packages/foo/run_tests.sh',
- ]),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.createComment(
- Config.packagesSlug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
-
- test('Schedule tasks when pull request is closed and merged', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'closed',
- number: issueNumber,
- merged: true,
- baseSha: 'sha1', // Found in pre-populated commits in FakeGerritService.
- mergeCommitSha: 'sha2',
- );
-
- expect(db.values.values.whereType<Commit>().length, 0);
- await tester.post(webhook);
- expect(db.values.values.whereType<Commit>().length, 1);
- });
-
- test('Fail when pull request is closed and merged, but merged commit is not found on GoB', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'closed',
- number: issueNumber,
- merged: true,
- baseSha: 'unknown_sha',
- );
-
- expect(db.values.values.whereType<Commit>().length, 0);
- try {
- await tester.post(webhook);
- } catch (e) {
- expect(
- e.toString(),
- matches(
- r'HTTP 500: (.+) was not found on GoB\. Failing so this event can be retried\.\.\.',
- ),
- );
- }
- expect(db.values.values.whereType<Commit>().length, 0);
- });
-
- test('Does not comment about needing tests on draft pull requests.', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- isDraft: true,
- );
-
- when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.value(
- PullRequestFile()..filename = 'some_change.dart',
- ),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.createComment(
- Config.flutterSlug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
-
- test('Will not spawn comments if they have already been made.', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- );
-
- when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer(
- (_) => Stream<PullRequestFile>.value(
- PullRequestFile()..filename = 'packages/flutter/blah.dart',
- ),
- );
-
- when(issuesService.listCommentsByIssue(Config.flutterSlug, issueNumber)).thenAnswer(
- (_) => Stream<IssueComment>.value(
- IssueComment()..body = config.missingTestsPullRequestMessageValue,
- ),
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.addLabelsToIssue(
- Config.flutterSlug,
- issueNumber,
- any,
- ),
- );
-
- verifyNever(
- issuesService.createComment(
- Config.flutterSlug,
- issueNumber,
- argThat(contains(config.missingTestsPullRequestMessageValue)),
- ),
- );
- });
-
- test('Skips labeling or commenting on autorolls', () async {
- const int issueNumber = 123;
-
- tester.message = generateGithubWebhookMessage(
- action: 'opened',
- number: issueNumber,
- login: 'engine-flutter-autoroll',
- );
-
- await tester.post(webhook);
-
- verifyNever(
- issuesService.createComment(
- any,
- issueNumber,
- any,
- ),
- );
- });
-
- test('Comments on PR but does not schedule builds for unmergeable PRs', () async {
- const int issueNumber = 12345;
- tester.message = generateGithubWebhookMessage(
- action: 'synchronize',
- number: issueNumber,
- // This PR is unmergeable (probably merge conflict)
- mergeable: false,
- );
-
- await tester.post(webhook);
- verify(
- issuesService.createComment(
- Config.flutterSlug,
- issueNumber,
- config.mergeConflictPullRequestMessage,
- ),
- );
- });
-
- test('When synchronized, cancels existing builds and schedules new ones', () async {
- const int issueNumber = 12345;
- bool batchRequestCalled = false;
- Future<BatchResponse> getBatchResponse() async {
- batchRequestCalled = true;
- return BatchResponse(
- responses: <Response>[
- Response(
- searchBuilds: SearchBuildsResponse(
- builds: <Build>[
- generateBuild(999, name: 'Linux', status: Status.ended),
- ],
- ),
- ),
- Response(
- searchBuilds: SearchBuildsResponse(
- builds: <Build>[
- generateBuild(998, name: 'Linux', status: Status.ended),
- ],
- ),
- ),
- ],
- );
- }
-
- fakeBuildBucketClient.batchResponse = getBatchResponse;
-
- tester.message = generateGithubWebhookMessage(
- action: 'synchronize',
- number: issueNumber,
- );
-
- final MockRepositoriesService mockRepositoriesService = MockRepositoriesService();
- when(gitHubClient.repositories).thenReturn(mockRepositoriesService);
-
- await tester.post(webhook);
- expect(batchRequestCalled, isTrue);
- });
-
- group('BuildBucket', () {
- const int issueNumber = 123;
-
- Future<void> testActions(String action) async {
- when(issuesService.listLabelsByIssue(any, issueNumber)).thenAnswer((_) {
- return Stream<IssueLabel>.fromIterable(<IssueLabel>[
- IssueLabel()..name = 'Random Label',
- ]);
- });
-
- fakeBuildBucketClient.batchResponse = () => Future<BatchResponse>.value(
- const BatchResponse(
- responses: <Response>[
- Response(
- searchBuilds: SearchBuildsResponse(
- builds: <Build>[],
- ),
- ),
- Response(
- searchBuilds: SearchBuildsResponse(
- builds: <Build>[],
- ),
- ),
- ],
- ),
- );
-
- tester.message = generateGithubWebhookMessage(
- action: action,
- number: 1,
- );
-
- await tester.post(webhook);
- }
-
- test('Edited Action works properly', () async {
- await testActions('edited');
- });
-
- test('Opened Action works properly', () async {
- await testActions('opened');
- });
-
- test('Ready_for_review Action works properly', () async {
- await testActions('ready_for_review');
- });
-
- test('Reopened Action works properly', () async {
- await testActions('reopened');
- });
-
- test('Labeled Action works properly', () async {
- await testActions('labeled');
- });
-
- test('Synchronize Action works properly', () async {
- await testActions('synchronize');
- });
-
- test('Comments on PR but does not schedule builds for unmergeable PRs', () async {
- when(issuesService.listCommentsByIssue(any, any)).thenAnswer((_) => Stream<IssueComment>.value(IssueComment()));
- tester.message = generateGithubWebhookMessage(
- action: 'synchronize',
- number: issueNumber,
- // This PR is unmergeable (probably merge conflict)
- mergeable: false,
- );
- await tester.post(webhook);
- verify(
- issuesService.createComment(
- Config.flutterSlug,
- issueNumber,
- config.mergeConflictPullRequestMessage,
- ),
- );
- });
-
- test('When synchronized, cancels existing builds and schedules new ones', () async {
- fakeBuildBucketClient.batchResponse = () => Future<BatchResponse>.value(
- BatchResponse(
- responses: <Response>[
- Response(
- searchBuilds: SearchBuildsResponse(
- builds: <Build>[
- generateBuild(999, name: 'Linux', status: Status.ended),
- ],
- ),
- ),
- Response(
- searchBuilds: SearchBuildsResponse(
- builds: <Build>[
- generateBuild(998, name: 'Linux', status: Status.ended),
- ],
- ),
- ),
- ],
- ),
- );
-
- tester.message = generateGithubWebhookMessage(
- action: 'synchronize',
- number: issueNumber,
- );
- final MockRepositoriesService mockRepositoriesService = MockRepositoriesService();
- when(gitHubClient.repositories).thenReturn(mockRepositoriesService);
-
- await tester.post(webhook);
- });
- });
- });
-
- group('github webhook check_run event', () {
- test('processes check run event', () async {
- tester.message = generateCheckRunEvent();
-
- await tester.post(webhook);
- });
-
- test('processes completed check run event', () async {
- tester.message = generateCheckRunEvent(
- action: 'completed',
- numberOfPullRequests: 0,
- );
-
- await tester.post(webhook);
- });
- });
-
- group('github webhook push event', () {
- test('handles push events for flutter/flutter beta branch', () async {
- tester.message = generatePushMessage('beta', 'flutter', 'flutter');
-
- await tester.post(webhook);
-
- verify(commitService.handlePushGithubRequest(any)).called(1);
- });
-
- test('handles push events for flutter/flutter stable branch', () async {
- tester.message = generatePushMessage('stable', 'flutter', 'flutter');
-
- await tester.post(webhook);
-
- verify(commitService.handlePushGithubRequest(any)).called(1);
- });
-
- test('does not handle push events for branches that are not beta|stable', () async {
- tester.message = generatePushMessage('main', 'flutter', 'flutter');
-
- await tester.post(webhook);
-
- verifyNever(commitService.handlePushGithubRequest(any)).called(0);
- });
-
- test('does not handle push events for repositories that are not flutter/flutter', () async {
- tester.message = generatePushMessage('beta', 'flutter', 'engine');
-
- await tester.post(webhook);
-
- verifyNever(commitService.handlePushGithubRequest(any)).called(0);
- });
- });
-
- group('github webhook create event', () {
- test('Does not create a new commit due to not being a candidate branch', () async {
- tester.message = generateCreateBranchMessage(
- 'cool-branch',
- 'flutter/flutter',
- );
-
- await tester.post(webhook);
-
- verifyNever(commitService.handleCreateGithubRequest(any)).called(0);
- });
-
- test('Creates a new commit due to being a candidate branch', () async {
- tester.message = generateCreateBranchMessage(
- 'flutter-1.2-candidate.3',
- 'flutter/flutter',
- );
-
- await tester.post(webhook);
-
- verify(commitService.handleCreateGithubRequest(any)).called(1);
- });
- });
-}
diff --git a/app_dart/test/request_handlers/github_webhook_test.dart b/app_dart/test/request_handlers/github_webhook_test.dart
deleted file mode 100644
index dceeaa8..0000000
--- a/app_dart/test/request_handlers/github_webhook_test.dart
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright 2019 The Flutter 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:convert';
-import 'dart:typed_data';
-
-import 'package:cocoon_service/cocoon_service.dart';
-import 'package:cocoon_service/protos.dart';
-import 'package:cocoon_service/src/request_handling/exceptions.dart';
-
-import 'package:crypto/crypto.dart';
-import 'package:test/test.dart';
-
-import '../src/datastore/fake_config.dart';
-import '../src/request_handling/fake_http.dart';
-import '../src/request_handling/fake_pubsub.dart';
-import '../src/request_handling/request_handler_tester.dart';
-
-void main() {
- late GithubWebhook webhook;
- late FakeConfig config;
- late FakeHttpRequest request;
- late FakePubSub pubsub;
- late RequestHandlerTester tester;
- const String keyString = 'not_a_real_key';
-
- String getHmac(Uint8List list, Uint8List key) {
- final Hmac hmac = Hmac(sha1, key);
- return hmac.convert(list).toString();
- }
-
- setUp(() {
- request = FakeHttpRequest();
- tester = RequestHandlerTester(request: request);
-
- config = FakeConfig(
- webhookKeyValue: keyString,
- );
- pubsub = FakePubSub();
-
- webhook = GithubWebhook(
- config: config,
- pubsub: pubsub,
- secret: config.webhookKey,
- topic: 'github-webhooks',
- );
- });
-
- test('Rejects non-POST methods with methodNotAllowed', () async {
- expect(tester.get(webhook), throwsA(isA<MethodNotAllowed>()));
- expect(pubsub.messages, isEmpty);
- });
-
- test('Rejects missing headers', () async {
- expect(tester.post(webhook), throwsA(isA<BadRequestException>()));
- expect(pubsub.messages, isEmpty);
- });
-
- test('Rejects invalid hmac', () async {
- request.headers.set('X-GitHub-Event', 'pull_request');
- request.headers.set('X-Hub-Signature', 'bar');
- request.body = 'Hello, World!';
- expect(tester.post(webhook), throwsA(isA<Forbidden>()));
- expect(pubsub.messages, isEmpty);
- });
-
- test('Publishes message', () async {
- request.headers.set('X-GitHub-Event', 'pull_request');
- request.body = '{}';
- final Uint8List body = utf8.encode(request.body!);
- final Uint8List key = utf8.encode(keyString);
- final String hmac = getHmac(body, key);
- request.headers.set('X-Hub-Signature', 'sha1=$hmac');
- await tester.post(webhook);
-
- expect(pubsub.messages, hasLength(1));
- final Map<String, dynamic> messageJson = pubsub.messages.single;
- final GithubWebhookMessage message = GithubWebhookMessage.fromJson(jsonEncode(messageJson));
- expect(message.event, 'pull_request');
- expect(message.payload, '{}');
- });
-}
diff --git a/app_dart/test/request_handlers/postsubmit_luci_subscription_test.dart b/app_dart/test/request_handlers/postsubmit_luci_subscription_test.dart
deleted file mode 100644
index 17ef648..0000000
--- a/app_dart/test/request_handlers/postsubmit_luci_subscription_test.dart
+++ /dev/null
@@ -1,357 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:cocoon_service/cocoon_service.dart';
-import 'package:cocoon_service/src/model/appengine/commit.dart';
-import 'package:cocoon_service/src/model/appengine/task.dart';
-import 'package:cocoon_service/src/request_handling/exceptions.dart';
-import 'package:cocoon_service/src/service/datastore.dart';
-import 'package:gcloud/db.dart';
-import 'package:mockito/mockito.dart';
-import 'package:test/test.dart';
-
-import '../src/datastore/fake_config.dart';
-import '../src/request_handling/fake_authentication.dart';
-import '../src/request_handling/fake_http.dart';
-import '../src/request_handling/subscription_tester.dart';
-import '../src/service/fake_luci_build_service.dart';
-import '../src/service/fake_scheduler.dart';
-import '../src/utilities/entity_generators.dart';
-import '../src/utilities/mocks.dart';
-import '../src/utilities/push_message.dart';
-
-void main() {
- late PostsubmitLuciSubscription handler;
- late FakeConfig config;
- late FakeHttpRequest request;
- late SubscriptionTester tester;
- late MockGithubChecksService mockGithubChecksService;
- late MockGithubChecksUtil mockGithubChecksUtil;
- late FakeScheduler scheduler;
-
- setUp(() async {
- config = FakeConfig(maxLuciTaskRetriesValue: 3);
- mockGithubChecksUtil = MockGithubChecksUtil();
- mockGithubChecksService = MockGithubChecksService();
- when(mockGithubChecksService.githubChecksUtil).thenReturn(mockGithubChecksUtil);
- when(mockGithubChecksUtil.createCheckRun(any, any, any, any, output: anyNamed('output')))
- .thenAnswer((_) async => generateCheckRun(1, name: 'Linux A'));
- when(mockGithubChecksService.updateCheckStatus(any, any, any)).thenAnswer((_) async => true);
- final FakeLuciBuildService luciBuildService = FakeLuciBuildService(
- config: config,
- githubChecksUtil: mockGithubChecksUtil,
- );
- scheduler = FakeScheduler(
- ciYaml: exampleConfig,
- config: config,
- luciBuildService: luciBuildService,
- );
- handler = PostsubmitLuciSubscription(
- cache: CacheService(inMemory: true),
- config: config,
- authProvider: FakeAuthenticationProvider(),
- githubChecksService: mockGithubChecksService,
- datastoreProvider: (_) => DatastoreService(config.db, 5),
- scheduler: scheduler,
- );
- request = FakeHttpRequest();
-
- tester = SubscriptionTester(
- request: request,
- );
- });
-
- test('throws exception when task key is not in message', () async {
- tester.message = createBuildbucketPushMessage(
- 'COMPLETED',
- result: 'SUCCESS',
- builderName: '',
- userData: '{\\"commit_key\\":\\"flutter/main/abc123\\"}',
- );
-
- expect(() => tester.post(handler), throwsA(isA<BadRequestException>()));
- });
-
- test('throws exception if task key does not exist in datastore', () {
- final Task task = generateTask(1);
- tester.message = createBuildbucketPushMessage(
- 'COMPLETED',
- result: 'SUCCESS',
- userData: '{\\"task_key\\":\\"${task.key.id}\\", \\"commit_key\\":\\"${task.key.parent?.id}\\"}',
- );
-
- expect(() => tester.post(handler), throwsA(isA<KeyNotFoundException>()));
- });
-
- test('updates task based on message', () async {
- final Commit commit = generateCommit(1, sha: '87f88734747805589f2131753620d61b22922822');
- final Task task = generateTask(
- 4507531199512576,
- parent: commit,
- name: 'Linux A',
- );
-
- tester.message = createBuildbucketPushMessage(
- 'COMPLETED',
- result: 'SUCCESS',
- userData: '{\\"task_key\\":\\"${task.key.id}\\", \\"commit_key\\":\\"${task.key.parent?.id}\\"}',
- );
-
- config.db.values[commit.key] = commit;
- config.db.values[task.key] = task;
-
- expect(task.status, Task.statusNew);
- expect(task.endTimestamp, 0);
-
- await tester.post(handler);
-
- expect(task.status, Task.statusSucceeded);
- expect(task.endTimestamp, 1565049193786);
- });
-
- test('skips task processing when build is with scheduled status', () async {
- final Commit commit = generateCommit(1, sha: '87f88734747805589f2131753620d61b22922822');
- final Task task = generateTask(
- 4507531199512576,
- name: 'Linux A',
- parent: commit,
- status: Task.statusInProgress,
- );
- config.db.values[task.key] = task;
- config.db.values[commit.key] = commit;
- tester.message = createBuildbucketPushMessage(
- 'SCHEDULED',
- builderName: 'Linux A',
- result: null,
- userData: '{\\"task_key\\":\\"${task.key.id}\\", \\"commit_key\\":\\"${task.key.parent?.id}\\"}',
- );
-
- expect(task.status, Task.statusInProgress);
- expect(task.attempts, 1);
- expect(await tester.post(handler), Body.empty);
- expect(task.status, Task.statusInProgress);
- });
-
- test('skips task processing when task has already finished', () async {
- final Commit commit = generateCommit(1, sha: '87f88734747805589f2131753620d61b22922822');
- final Task task = generateTask(
- 4507531199512576,
- name: 'Linux A',
- parent: commit,
- status: Task.statusSucceeded,
- );
- config.db.values[task.key] = task;
- config.db.values[commit.key] = commit;
- tester.message = createBuildbucketPushMessage(
- 'STARTED',
- builderName: 'Linux A',
- result: null,
- userData: '{\\"task_key\\":\\"${task.key.id}\\", \\"commit_key\\":\\"${task.key.parent?.id}\\"}',
- );
-
- expect(task.status, Task.statusSucceeded);
- expect(task.attempts, 1);
- expect(await tester.post(handler), Body.empty);
- expect(task.status, Task.statusSucceeded);
- });
-
- test('skips task processing when target has been deleted', () async {
- final Commit commit = generateCommit(1, sha: '87f88734747805589f2131753620d61b22922822');
- final Task task = generateTask(
- 4507531199512576,
- name: 'Linux B',
- parent: commit,
- status: Task.statusSucceeded,
- );
- config.db.values[task.key] = task;
- config.db.values[commit.key] = commit;
- tester.message = createBuildbucketPushMessage(
- 'STARTED',
- builderName: 'Linux B',
- result: null,
- userData: '{\\"task_key\\":\\"${task.key.id}\\", \\"commit_key\\":\\"${task.key.parent?.id}\\"}',
- );
-
- expect(task.status, Task.statusSucceeded);
- expect(task.attempts, 1);
- expect(await tester.post(handler), Body.empty);
- });
-
- test('does not fail on empty user data', () async {
- tester.message = createBuildbucketPushMessage(
- 'COMPLETED',
- result: 'SUCCESS',
- userData: null,
- );
-
- expect(await tester.post(handler), Body.empty);
- });
-
- test('on failed builds auto-rerun the build', () async {
- final Commit commit = generateCommit(1, sha: '87f88734747805589f2131753620d61b22922822');
- final Task task = generateTask(
- 4507531199512576,
- name: 'Linux A',
- parent: commit,
- status: Task.statusFailed,
- );
- config.db.values[task.key] = task;
- config.db.values[commit.key] = commit;
- tester.message = createBuildbucketPushMessage(
- 'COMPLETED',
- builderName: 'Linux A',
- result: 'FAILURE',
- userData: '{\\"task_key\\":\\"${task.key.id}\\", \\"commit_key\\":\\"${task.key.parent?.id}\\"}',
- );
-
- expect(task.status, Task.statusFailed);
- expect(task.attempts, 1);
- expect(await tester.post(handler), Body.empty);
- expect(task.status, Task.statusInProgress);
- expect(task.attempts, 2);
- });
-
- test('on canceled builds auto-rerun the build if they timed out', () async {
- final Commit commit = generateCommit(1, sha: '87f88734747805589f2131753620d61b22922822');
- final Task task = generateTask(
- 4507531199512576,
- name: 'Linux A',
- parent: commit,
- status: Task.statusInfraFailure,
- );
- config.db.values[task.key] = task;
- config.db.values[commit.key] = commit;
- tester.message = createBuildbucketPushMessage(
- 'COMPLETED',
- builderName: 'Linux A',
- result: 'CANCELED',
- userData: '{\\"task_key\\":\\"${task.key.id}\\", \\"commit_key\\":\\"${task.key.parent?.id}\\"}',
- );
-
- expect(task.status, Task.statusInfraFailure);
- expect(task.attempts, 1);
- expect(await tester.post(handler), Body.empty);
- expect(task.status, Task.statusInProgress);
- expect(task.attempts, 2);
- });
-
- test('on builds resulting in an infra failure auto-rerun the build if they timed out', () async {
- final Commit commit = generateCommit(1, sha: '87f88734747805589f2131753620d61b22922822');
- final Task task = generateTask(
- 4507531199512576,
- name: 'Linux A',
- parent: commit,
- status: Task.statusInfraFailure,
- );
- config.db.values[task.key] = task;
- config.db.values[commit.key] = commit;
- tester.message = createBuildbucketPushMessage(
- 'COMPLETED',
- builderName: 'Linux A',
- result: 'FAILURE',
- failureReason: 'INFRA_FAILURE',
- userData: '{\\"task_key\\":\\"${task.key.id}\\", \\"commit_key\\":\\"${task.key.parent?.id}\\"}',
- );
-
- expect(task.status, Task.statusInfraFailure);
- expect(task.attempts, 1);
- expect(await tester.post(handler), Body.empty);
- expect(task.status, Task.statusInProgress);
- expect(task.attempts, 2);
- });
-
- test('fallback to build parameters if task_key is not present', () async {
- final Commit commit = generateCommit(1, sha: '87f88734747805589f2131753620d61b22922822');
- final Task task = generateTask(
- 4507531199512576,
- name: 'Linux A',
- parent: commit,
- status: Task.statusNew,
- );
- config.db.values[task.key] = task;
- config.db.values[commit.key] = commit;
- tester.message = createBuildbucketPushMessage(
- 'COMPLETED',
- builderName: 'Linux A',
- result: 'FAILURE',
- userData: '{\\"task_key\\":\\"null\\", \\"commit_key\\":\\"${task.key.parent?.id}\\"}',
- );
-
- expect(task.status, Task.statusNew);
- expect(await tester.post(handler), Body.empty);
- expect(task.status, Task.statusInProgress);
- });
-
- test('non-bringup target updates check run', () async {
- scheduler.ciYaml = nonBringupPackagesConfig;
- when(mockGithubChecksService.updateCheckStatus(any, any, any)).thenAnswer((_) async => true);
- final Commit commit = generateCommit(1, sha: '87f88734747805589f2131753620d61b22922822', repo: 'packages');
- final Task task = generateTask(
- 4507531199512576,
- name: 'Linux nonbringup',
- parent: commit,
- );
- config.db.values[commit.key] = commit;
- config.db.values[task.key] = task;
-
- tester.message = createBuildbucketPushMessage(
- 'COMPLETED',
- result: 'SUCCESS',
- builderName: 'Linux A',
- // Use escaped string to mock json decoded ones.
- userData:
- '{\\"task_key\\":\\"${task.key.id}\\", \\"commit_key\\":\\"${task.key.parent?.id}\\", \\"repo_owner\\": \\"flutter\\", \\"repo_name\\": \\"packages\\"}',
- );
- await tester.post(handler);
- verify(mockGithubChecksService.updateCheckStatus(any, any, any)).called(1);
- });
-
- test('bringup target does not update check run', () async {
- scheduler.ciYaml = bringupPackagesConfig;
- when(mockGithubChecksService.updateCheckStatus(any, any, any)).thenAnswer((_) async => true);
- final Commit commit = generateCommit(1, sha: '87f88734747805589f2131753620d61b22922822');
- final Task task = generateTask(
- 4507531199512576,
- name: 'Linux bringup',
- parent: commit,
- );
- config.db.values[commit.key] = commit;
- config.db.values[task.key] = task;
-
- tester.message = createBuildbucketPushMessage(
- 'COMPLETED',
- result: 'SUCCESS',
- builderName: 'Linux bringup',
- // Use escaped string to mock json decoded ones.
- userData:
- '{\\"task_key\\":\\"${task.key.id}\\", \\"commit_key\\":\\"${task.key.parent?.id}\\", \\"repo_owner\\": \\"flutter\\", \\"repo_name\\": \\"packages\\"}',
- );
- await tester.post(handler);
- verifyNever(mockGithubChecksService.updateCheckStatus(any, any, any));
- });
-
- test('unsupported repo target does not update check run', () async {
- scheduler.ciYaml = unsupportedPostsubmitCheckrunConfig;
- when(mockGithubChecksService.updateCheckStatus(any, any, any)).thenAnswer((_) async => true);
- final Commit commit = generateCommit(1, sha: '87f88734747805589f2131753620d61b22922822');
- final Task task = generateTask(
- 4507531199512576,
- name: 'Linux flutter',
- parent: commit,
- );
- config.db.values[commit.key] = commit;
- config.db.values[task.key] = task;
-
- tester.message = createBuildbucketPushMessage(
- 'COMPLETED',
- result: 'SUCCESS',
- builderName: 'Linux bringup',
- // Use escaped string to mock json decoded ones.
- userData:
- '{\\"task_key\\":\\"${task.key.id}\\", \\"commit_key\\":\\"${task.key.parent?.id}\\", \\"repo_owner\\": \\"flutter\\", \\"repo_name\\": \\"flutter\\"}',
- );
- await tester.post(handler);
- verifyNever(mockGithubChecksService.updateCheckStatus(any, any, any));
- });
-}
diff --git a/app_dart/test/request_handlers/presubmit_luci_subscription_test.dart b/app_dart/test/request_handlers/presubmit_luci_subscription_test.dart
deleted file mode 100644
index 87e7cb7..0000000
--- a/app_dart/test/request_handlers/presubmit_luci_subscription_test.dart
+++ /dev/null
@@ -1,186 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:cocoon_service/cocoon_service.dart';
-import 'package:cocoon_service/src/model/luci/buildbucket.dart' as bb;
-import 'package:cocoon_service/src/model/luci/push_message.dart';
-import 'package:mockito/mockito.dart';
-import 'package:test/test.dart';
-
-import '../src/datastore/fake_config.dart';
-import '../src/request_handling/fake_authentication.dart';
-import '../src/request_handling/fake_http.dart';
-import '../src/request_handling/subscription_tester.dart';
-import '../src/service/fake_buildbucket.dart';
-import '../src/service/fake_luci_build_service.dart';
-import '../src/service/fake_scheduler.dart';
-import '../src/utilities/mocks.dart';
-import '../src/utilities/push_message.dart';
-
-const String ref = 'deadbeef';
-
-void main() {
- late PresubmitLuciSubscription handler;
- late FakeBuildBucketClient buildbucket;
- late FakeConfig config;
- late MockGitHub mockGitHubClient;
- late FakeHttpRequest request;
- late SubscriptionTester tester;
- late MockRepositoriesService mockRepositoriesService;
- late MockGithubChecksService mockGithubChecksService;
- late MockLuciBuildService mockLuciBuildService;
- late FakeScheduler scheduler;
-
- setUp(() async {
- config = FakeConfig();
- buildbucket = FakeBuildBucketClient();
- mockLuciBuildService = MockLuciBuildService();
-
- mockGithubChecksService = MockGithubChecksService();
- scheduler = FakeScheduler(
- ciYaml: examplePresubmitRescheduleConfig,
- config: config,
- luciBuildService: mockLuciBuildService,
- );
- handler = PresubmitLuciSubscription(
- cache: CacheService(inMemory: true),
- config: config,
- buildBucketClient: buildbucket,
- luciBuildService: FakeLuciBuildService(config: config),
- githubChecksService: mockGithubChecksService,
- authProvider: FakeAuthenticationProvider(),
- scheduler: scheduler,
- );
- request = FakeHttpRequest();
-
- tester = SubscriptionTester(
- request: request,
- );
-
- mockGitHubClient = MockGitHub();
- mockRepositoriesService = MockRepositoriesService();
- when(mockGitHubClient.repositories).thenReturn(mockRepositoriesService);
- config.githubClient = mockGitHubClient;
- });
-
- test('Requests without repo_owner and repo_name do not update checks', () async {
- tester.message = pushMessageJsonNoBuildset(
- 'COMPLETED',
- result: 'SUCCESS',
- builderName: 'Linux Host Engine',
- );
-
- await tester.post(handler);
- verifyNever(mockGithubChecksService.updateCheckStatus(any, any, any));
- });
-
- test('Requests with repo_owner and repo_name update checks', () async {
- when(mockGithubChecksService.updateCheckStatus(any, any, any)).thenAnswer((_) async => true);
- when(mockGithubChecksService.taskFailed(any)).thenAnswer((_) => false);
- tester.message = createBuildbucketPushMessage(
- 'COMPLETED',
- result: 'SUCCESS',
- builderName: 'Linux Host Engine',
- userData: '{\\"repo_owner\\": \\"flutter\\", \\"repo_name\\": \\"cocoon\\"}',
- );
- await tester.post(handler);
- verify(mockGithubChecksService.updateCheckStatus(any, any, any)).called(1);
- });
-
- test('Requests when task failed but no need to reschedule', () async {
- when(mockGithubChecksService.updateCheckStatus(any, any, any)).thenAnswer((_) async => true);
- when(mockGithubChecksService.taskFailed(any)).thenAnswer((_) => true);
- when(mockGithubChecksService.currentAttempt(any)).thenAnswer((_) => 1);
- tester.message = createBuildbucketPushMessage(
- 'COMPLETED',
- result: 'SUCCESS',
- builderName: 'Linux A',
- userData: '{\\"repo_owner\\": \\"flutter\\",'
- '\\"commit_branch\\": \\"main\\",'
- '\\"commit_sha\\": \\"abc\\",'
- '\\"repo_name\\": \\"flutter\\"}',
- );
- when(
- mockLuciBuildService.rescheduleBuild(
- builderName: 'Linux Coverage',
- buildPushMessage: BuildPushMessage.fromPushMessage(tester.message),
- rescheduleAttempt: 0,
- ),
- ).thenAnswer(
- (_) async => const bb.Build(
- id: '8905920700440101120',
- builderId: bb.BuilderId(bucket: 'luci.flutter.prod', project: 'flutter', builder: 'Linux Coverage'),
- ),
- );
- await tester.post(handler);
- verifyNever(
- mockLuciBuildService.rescheduleBuild(
- builderName: 'Linux Coverage',
- buildPushMessage: BuildPushMessage.fromPushMessage(tester.message),
- rescheduleAttempt: 0,
- ),
- );
- verify(mockGithubChecksService.updateCheckStatus(any, any, any)).called(1);
- });
- test('Requests when task failed but need to reschedule', () async {
- when(mockGithubChecksService.updateCheckStatus(any, any, any, rescheduled: true)).thenAnswer((_) async => true);
- when(mockGithubChecksService.taskFailed(any)).thenAnswer((_) => true);
- when(mockGithubChecksService.currentAttempt(any)).thenAnswer((_) => 0);
- tester.message = createBuildbucketPushMessage(
- 'COMPLETED',
- result: 'SUCCESS',
- builderName: 'Linux B',
- userData: '{\\"repo_owner\\": \\"flutter\\",'
- '\\"commit_branch\\": \\"main\\",'
- '\\"commit_sha\\": \\"abc\\",'
- '\\"repo_name\\": \\"flutter\\"}',
- );
- when(
- mockLuciBuildService.rescheduleBuild(
- builderName: 'Linux Coverage',
- buildPushMessage: BuildPushMessage.fromPushMessage(tester.message),
- rescheduleAttempt: 1,
- ),
- ).thenAnswer(
- (_) async => const bb.Build(
- id: '8905920700440101120',
- builderId: bb.BuilderId(bucket: 'luci.flutter.prod', project: 'flutter', builder: 'Linux B'),
- ),
- );
- await tester.post(handler);
- verifyNever(
- mockLuciBuildService.rescheduleBuild(
- builderName: 'Linux B',
- buildPushMessage: BuildPushMessage.fromPushMessage(tester.message),
- rescheduleAttempt: 1,
- ),
- );
- verify(mockGithubChecksService.updateCheckStatus(any, any, any, rescheduled: true)).called(1);
- });
-
- test('Build not rescheduled if not found in ciYaml list.', () async {
- when(mockGithubChecksService.updateCheckStatus(any, any, any, rescheduled: false)).thenAnswer((_) async => true);
- when(mockGithubChecksService.taskFailed(any)).thenAnswer((_) => true);
- when(mockGithubChecksService.currentAttempt(any)).thenAnswer((_) => 1);
- tester.message = createBuildbucketPushMessage(
- 'COMPLETED',
- result: 'SUCCESS',
- // This builder will not be present.
- builderName: 'Linux C',
- userData: '{\\"repo_owner\\": \\"flutter\\",'
- '\\"commit_branch\\": \\"main\\",'
- '\\"commit_sha\\": \\"abc\\",'
- '\\"repo_name\\": \\"flutter\\"}',
- );
- await tester.post(handler);
- verifyNever(
- mockLuciBuildService.rescheduleBuild(
- builderName: 'Linux C',
- buildPushMessage: BuildPushMessage.fromPushMessage(tester.message),
- rescheduleAttempt: 1,
- ),
- );
- verify(mockGithubChecksService.updateCheckStatus(any, any, any, rescheduled: false)).called(1);
- });
-}
diff --git a/app_dart/test/request_handlers/push_build_status_to_github_test.dart b/app_dart/test/request_handlers/push_build_status_to_github_test.dart
deleted file mode 100644
index 17eff2f..0000000
--- a/app_dart/test/request_handlers/push_build_status_to_github_test.dart
+++ /dev/null
@@ -1,169 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:cocoon_service/src/model/appengine/github_build_status_update.dart';
-import 'package:cocoon_service/src/request_handlers/push_build_status_to_github.dart';
-import 'package:cocoon_service/src/request_handling/body.dart';
-import 'package:cocoon_service/src/service/build_status_provider.dart';
-import 'package:cocoon_service/src/service/config.dart';
-import 'package:cocoon_service/src/service/datastore.dart';
-import 'package:gcloud/db.dart';
-import 'package:github/github.dart';
-import 'package:googleapis/bigquery/v2.dart' hide Model;
-import 'package:mockito/mockito.dart';
-import 'package:test/test.dart';
-
-import '../src/bigquery/fake_tabledata_resource.dart';
-import '../src/datastore/fake_config.dart';
-import '../src/datastore/fake_datastore.dart';
-import '../src/request_handling/api_request_handler_tester.dart';
-import '../src/request_handling/fake_authentication.dart';
-import '../src/service/fake_build_status_provider.dart';
-import '../src/service/fake_github_service.dart';
-import '../src/utilities/entity_generators.dart';
-import '../src/utilities/mocks.dart';
-
-void main() {
- group('PushStatusToGithub', () {
- late FakeBuildStatusService buildStatusService;
- late FakeClientContext clientContext;
- late FakeConfig config;
- late FakeDatastoreDB db;
- late ApiRequestHandlerTester tester;
- late FakeAuthenticatedContext authContext;
- late FakeTabledataResource tabledataResourceApi;
- late PushBuildStatusToGithub handler;
- late MockGitHub github;
- late MockPullRequestsService pullRequestsService;
- late MockIssuesService issuesService;
- late MockRepositoriesService repositoriesService;
- late FakeGithubService githubService;
-
- GithubBuildStatusUpdate newStatusUpdate(PullRequest pr, BuildStatus status) {
- return GithubBuildStatusUpdate(
- key: db.emptyKey.append(GithubBuildStatusUpdate, id: pr.number),
- repository: Config.flutterSlug.fullName,
- status: status.githubStatus,
- pr: pr.number!,
- head: pr.head!.sha,
- updates: 0,
- );
- }
-
- setUp(() async {
- clientContext = FakeClientContext();
- authContext = FakeAuthenticatedContext(clientContext: clientContext);
- clientContext.isDevelopmentEnvironment = false;
- buildStatusService = FakeBuildStatusService();
- githubService = FakeGithubService();
- tabledataResourceApi = FakeTabledataResource();
- db = FakeDatastoreDB();
- github = MockGitHub();
- pullRequestsService = MockPullRequestsService();
- issuesService = MockIssuesService();
- repositoriesService = MockRepositoriesService();
- config = FakeConfig(
- tabledataResource: tabledataResourceApi,
- githubService: githubService,
- dbValue: db,
- githubClient: github,
- );
- tester = ApiRequestHandlerTester(context: authContext);
- handler = PushBuildStatusToGithub(
- config: config,
- authenticationProvider: FakeAuthenticationProvider(clientContext: clientContext),
- buildStatusServiceProvider: (_) => buildStatusService,
- datastoreProvider: (DatastoreDB db) => DatastoreService(config.db, 5),
- );
-
- when(github.pullRequests).thenReturn(pullRequestsService);
- when(github.issues).thenReturn(issuesService);
- when(github.repositories).thenReturn(repositoriesService);
- when(repositoriesService.createStatus(any, any, any)).thenAnswer(
- (_) async => RepositoryStatus(),
- );
- });
-
- test('development environment does nothing', () async {
- clientContext.isDevelopmentEnvironment = true;
- config.githubClient = ThrowingGitHub();
- db.onCommit = (List<Model<dynamic>> insert, List<Key<dynamic>> deletes) => throw AssertionError();
- db.addOnQuery<GithubBuildStatusUpdate>((Iterable<GithubBuildStatusUpdate> results) {
- throw AssertionError();
- });
- final Body body = await tester.get<Body>(handler);
- expect(body, same(Body.empty));
- });
-
- group('does not update anything', () {
- setUp(() {
- db.onCommit = (List<Model<dynamic>> insert, List<Key<dynamic>> deletes) => throw AssertionError();
- when(repositoriesService.createStatus(any, any, any)).thenThrow(AssertionError());
- });
-
- test('if there are no PRs', () async {
- when(pullRequestsService.list(any, base: anyNamed('base')))
- .thenAnswer((_) => const Stream<PullRequest>.empty());
- buildStatusService.cumulativeStatus = BuildStatus.success();
- final Body body = await tester.get<Body>(handler);
- final TableDataList tableDataList = await tabledataResourceApi.list('test', 'test', 'test');
- expect(body, same(Body.empty));
-
- // Test for BigQuery insert
- expect(tableDataList.totalRows, '1');
- });
-
- test('only if pull request is for the default branch', () async {
- when(pullRequestsService.list(any)).thenAnswer(
- (_) => Stream<PullRequest>.value(
- generatePullRequest(
- id: 1,
- branch: 'flutter-2.15-candidate.3',
- ),
- ),
- );
- buildStatusService.cumulativeStatus = BuildStatus.success();
- await tester.get<Body>(handler);
- verifyNever(repositoriesService.createStatus(any, any, any));
- });
-
- test('if status has not changed since last update', () async {
- final PullRequest pr = generatePullRequest(id: 1, sha: '1');
- when(pullRequestsService.list(any, base: anyNamed('base'))).thenAnswer((_) => Stream<PullRequest>.value(pr));
- buildStatusService.cumulativeStatus = BuildStatus.success();
- final GithubBuildStatusUpdate status = newStatusUpdate(pr, BuildStatus.success());
- db.values[status.key] = status;
- final Body body = await tester.get<Body>(handler);
- expect(body, same(Body.empty));
- expect(status.updates, 0);
- });
-
- test('if there is no pr found for a targeted branch', () async {
- final PullRequest pr = generatePullRequest(id: 1, sha: '1');
- when(pullRequestsService.list(any, base: anyNamed('base'))).thenAnswer((_) => Stream<PullRequest>.value(pr));
- buildStatusService.cumulativeStatus = BuildStatus.success();
- final GithubBuildStatusUpdate status =
- newStatusUpdate(pr, BuildStatus.failure(const <String>['failed_task_1']));
- db.values[status.key] = status;
- final Body body = await tester.get<Body>(handler);
- expect(body, same(Body.empty));
- expect(status.updates, 0);
- expect(status.status, BuildStatus.failure().githubStatus);
- });
- });
-
- test('updates github and datastore if status has changed since last update', () async {
- final PullRequest pr = generatePullRequest(id: 1, sha: '1');
- when(pullRequestsService.list(any, base: anyNamed('base'))).thenAnswer((_) => Stream<PullRequest>.value(pr));
- buildStatusService.cumulativeStatus = BuildStatus.success();
- final GithubBuildStatusUpdate status = newStatusUpdate(pr, BuildStatus.failure(const <String>['failed_test_1']));
- db.values[status.key] = status;
- final Body body = await tester.get<Body>(handler);
- expect(body, same(Body.empty));
- expect(status.updates, 1);
- expect(status.updateTimeMillis, isNotNull);
- expect(status.status, BuildStatus.success().githubStatus);
- });
- });
-}
diff --git a/app_dart/test/request_handlers/push_gold_status_to_github_test.dart b/app_dart/test/request_handlers/push_gold_status_to_github_test.dart
deleted file mode 100644
index 80eb758..0000000
--- a/app_dart/test/request_handlers/push_gold_status_to_github_test.dart
+++ /dev/null
@@ -1,1604 +0,0 @@
-// Copyright 2020 The Flutter 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:async';
-import 'dart:io';
-
-import 'package:cocoon_service/src/model/appengine/github_gold_status_update.dart';
-import 'package:cocoon_service/src/request_handlers/push_gold_status_to_github.dart';
-import 'package:cocoon_service/src/request_handling/body.dart';
-import 'package:cocoon_service/src/service/datastore.dart';
-import 'package:cocoon_service/src/service/logging.dart';
-import 'package:gcloud/db.dart' as gcloud_db;
-import 'package:gcloud/db.dart';
-import 'package:github/github.dart';
-import 'package:graphql/client.dart';
-import 'package:http/http.dart' as http;
-import 'package:http/testing.dart';
-import 'package:logging/logging.dart';
-import 'package:mockito/mockito.dart';
-import 'package:retry/retry.dart';
-import 'package:test/test.dart';
-
-import '../src/datastore/fake_config.dart';
-import '../src/datastore/fake_datastore.dart';
-import '../src/request_handling/api_request_handler_tester.dart';
-import '../src/request_handling/fake_authentication.dart';
-import '../src/service/fake_graphql_client.dart';
-import '../src/utilities/mocks.dart';
-
-void main() {
- const String kGoldenFileLabel = 'will affect goldens';
-
- group('PushGoldStatusToGithub', () {
- late FakeConfig config;
- late FakeClientContext clientContext;
- FakeAuthenticatedContext authContext;
- late FakeAuthenticationProvider auth;
- late FakeDatastoreDB db;
- late ApiRequestHandlerTester tester;
- late PushGoldStatusToGithub handler;
- FakeGraphQLClient githubGraphQLClient;
- List<dynamic> checkRuns = <dynamic>[];
- List<dynamic> engineCheckRuns = <dynamic>[];
- late MockClient mockHttpClient;
- late RepositorySlug slug;
- late RepositorySlug engineSlug;
- late RetryOptions retryOptions;
-
- final List<LogRecord> records = <LogRecord>[];
-
- setUp(() {
- clientContext = FakeClientContext();
- authContext = FakeAuthenticatedContext(clientContext: clientContext);
- auth = FakeAuthenticationProvider(clientContext: clientContext);
- githubGraphQLClient = FakeGraphQLClient();
- db = FakeDatastoreDB();
- config = FakeConfig(
- dbValue: db,
- );
- tester = ApiRequestHandlerTester(context: authContext);
- mockHttpClient = MockClient((_) async => http.Response('{}', HttpStatus.ok));
- retryOptions = const RetryOptions(
- delayFactor: Duration(microseconds: 1),
- maxDelay: Duration(microseconds: 2),
- maxAttempts: 2,
- );
-
- githubGraphQLClient.mutateResultForOptions = (MutationOptions options) => createFakeQueryResult();
- githubGraphQLClient.queryResultForOptions = (QueryOptions options) {
- if (options.variables['sRepoName'] == slug.name) {
- return createGithubQueryResult(checkRuns);
- }
- if (options.variables['sRepoName'] == engineSlug.name) {
- return createGithubQueryResult(engineCheckRuns);
- }
- return createGithubQueryResult(<dynamic>[]);
- };
- config.githubGraphQLClient = githubGraphQLClient;
- config.flutterGoldPendingValue = 'pending';
- config.flutterGoldChangesValue = 'changes';
- config.flutterGoldSuccessValue = 'success';
- config.flutterGoldAlertConstantValue = 'flutter gold alert';
- config.flutterGoldInitialAlertValue = 'initial';
- config.flutterGoldFollowUpAlertValue = 'follow-up';
- config.flutterGoldDraftChangeValue = 'draft';
- config.flutterGoldStalePRValue = 'stale';
-
- handler = PushGoldStatusToGithub(
- config: config,
- authenticationProvider: auth,
- datastoreProvider: (DatastoreDB db) {
- return DatastoreService(
- db,
- 5,
- retryOptions: retryOptions,
- );
- },
- goldClient: mockHttpClient,
- ingestionDelay: Duration.zero,
- );
-
- slug = RepositorySlug('flutter', 'flutter');
- engineSlug = RepositorySlug('flutter', 'engine');
- checkRuns.clear();
- engineCheckRuns.clear();
- records.clear();
- log.onRecord.listen((LogRecord record) => records.add(record));
- });
-
- group('in development environment', () {
- setUp(() {
- clientContext.isDevelopmentEnvironment = true;
- });
-
- test('Does nothing', () async {
- config.githubClient = ThrowingGitHub();
- db.onCommit =
- (List<gcloud_db.Model<dynamic>> insert, List<gcloud_db.Key<dynamic>> deletes) => throw AssertionError();
- db.addOnQuery<GithubGoldStatusUpdate>((Iterable<GithubGoldStatusUpdate> results) {
- throw AssertionError();
- });
- final Body body = await tester.get<Body>(handler);
- expect(body, same(Body.empty));
- });
- });
-
- group('in non-development environment', () {
- MockGitHub github;
- MockPullRequestsService pullRequestsService;
- late MockIssuesService issuesService;
- late MockRepositoriesService repositoriesService;
- List<PullRequest> prsFromGitHub = <PullRequest>[];
- List<PullRequest> enginePrsFromGitHub = <PullRequest>[];
-
- setUp(() {
- github = MockGitHub();
- pullRequestsService = MockPullRequestsService();
- issuesService = MockIssuesService();
- repositoriesService = MockRepositoriesService();
- when(github.pullRequests).thenReturn(pullRequestsService);
- when(github.issues).thenReturn(issuesService);
- when(github.repositories).thenReturn(repositoriesService);
-
- prsFromGitHub.clear();
- when(pullRequestsService.list(slug)).thenAnswer((Invocation _) {
- return Stream<PullRequest>.fromIterable(prsFromGitHub);
- });
-
- enginePrsFromGitHub.clear();
- when(pullRequestsService.list(engineSlug)).thenAnswer((Invocation _) {
- return Stream<PullRequest>.fromIterable(enginePrsFromGitHub);
- });
-
- when(repositoriesService.createStatus(any, any, any)).thenAnswer((_) async => RepositoryStatus());
- when(issuesService.createComment(any, any, any)).thenAnswer((_) async => IssueComment());
- when(issuesService.addLabelsToIssue(any, any, any)).thenAnswer((_) async => <IssueLabel>[]);
- config.githubClient = github;
- clientContext.isDevelopmentEnvironment = false;
- });
-
- GithubGoldStatusUpdate newStatusUpdate(
- RepositorySlug slug,
- PullRequest pr,
- String statusUpdate,
- String sha,
- String description,
- ) {
- return GithubGoldStatusUpdate(
- key: db.emptyKey.append(GithubGoldStatusUpdate, id: pr.number),
- status: statusUpdate,
- pr: pr.number!,
- head: sha,
- updates: 0,
- description: description,
- repository: slug.fullName,
- );
- }
-
- PullRequest newPullRequest(int number, String sha, String baseRef, {bool draft = false, DateTime? updated}) {
- return PullRequest()
- ..number = number
- ..head = (PullRequestHead()..sha = sha)
- ..base = (PullRequestHead()..ref = baseRef)
- ..draft = draft
- ..updatedAt = updated ?? DateTime.now();
- }
-
- group('does not update GitHub or Datastore', () {
- setUp(() {
- db.onCommit =
- (List<gcloud_db.Model<dynamic>> insert, List<gcloud_db.Key<dynamic>> deletes) => throw AssertionError();
- when(repositoriesService.createStatus(any, any, any)).thenThrow(AssertionError());
- });
-
- test('if there are no PRs', () async {
- prsFromGitHub = <PullRequest>[];
- final Body body = await tester.get<Body>(handler);
- expect(body, same(Body.empty));
- expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty);
- expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty);
- });
-
- test('if there are no framework or web engine tests for this PR', () async {
- checkRuns = <dynamic>[
- <String, String>{'name': 'tool-test1', 'status': 'completed', 'conclusion': 'success'},
- ];
- final PullRequest flutterPr = newPullRequest(123, 'abc', 'master');
- prsFromGitHub = <PullRequest>[flutterPr];
- final GithubGoldStatusUpdate status = newStatusUpdate(slug, flutterPr, '', '', '');
- db.values[status.key] = status;
-
- engineCheckRuns = <dynamic>[
- <String, String>{'name': 'linux-host1', 'status': 'completed', 'conclusion': 'success'},
- ];
- final PullRequest enginePr = newPullRequest(456, 'def', 'main');
- enginePrsFromGitHub = <PullRequest>[enginePr];
- final GithubGoldStatusUpdate engineStatus = newStatusUpdate(engineSlug, enginePr, '', '', '');
- db.values[engineStatus.key] = engineStatus;
-
- final Body body = await tester.get<Body>(handler);
- expect(body, same(Body.empty));
- expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty);
- expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty);
-
- // Should not apply labels or make comments
- verifyNever(
- issuesService.addLabelsToIssue(
- slug,
- flutterPr.number!,
- <String>[
- kGoldenFileLabel,
- ],
- ),
- );
- verifyNever(
- issuesService.addLabelsToIssue(
- engineSlug,
- enginePr.number!,
- <String>[
- kGoldenFileLabel,
- ],
- ),
- );
-
- verifyNever(
- issuesService.createComment(
- slug,
- flutterPr.number!,
- argThat(contains(config.flutterGoldCommentID(flutterPr))),
- ),
- );
- verifyNever(
- issuesService.createComment(
- engineSlug,
- enginePr.number!,
- argThat(contains(config.flutterGoldCommentID(enginePr))),
- ),
- );
- });
-
- test('if there are no framework tests for this PR, exclude web builds', () async {
- checkRuns = <dynamic>[
- <String, String>{'name': 'web-test1', 'status': 'completed', 'conclusion': 'success'},
- ];
- final PullRequest pr = newPullRequest(123, 'abc', 'master');
- prsFromGitHub = <PullRequest>[pr];
- final GithubGoldStatusUpdate status = newStatusUpdate(slug, pr, '', '', '');
- db.values[status.key] = status;
- final Body body = await tester.get<Body>(handler);
- expect(body, same(Body.empty));
- expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty);
- expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty);
-
- // Should not apply labels or make comments
- verifyNever(
- issuesService.addLabelsToIssue(
- slug,
- pr.number!,
- <String>[
- kGoldenFileLabel,
- ],
- ),
- );
-
- verifyNever(
- issuesService.createComment(
- slug,
- pr.number!,
- argThat(contains(config.flutterGoldCommentID(pr))),
- ),
- );
- });
-
- test('same commit, checks running, last status running', () async {
- // Same commit
- final PullRequest pr = newPullRequest(123, 'abc', 'master');
- prsFromGitHub = <PullRequest>[pr];
- final GithubGoldStatusUpdate status = newStatusUpdate(
- slug,
- pr,
- GithubGoldStatusUpdate.statusRunning,
- 'abc',
- config.flutterGoldPendingValue!,
- );
- db.values[status.key] = status;
-
- // Checks still running
- checkRuns = <dynamic>[
- <String, String>{'name': 'framework', 'status': 'in_progress', 'conclusion': 'neutral'},
- <String, String>{'name': 'web engine', 'status': 'in_progress', 'conclusion': 'neutral'},
- ];
-
- final Body body = await tester.get<Body>(handler);
- expect(body, same(Body.empty));
- expect(status.updates, 0);
- expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty);
- expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty);
-
- // Should not apply labels or make comments
- verifyNever(
- issuesService.addLabelsToIssue(
- slug,
- pr.number!,
- <String>[
- kGoldenFileLabel,
- ],
- ),
- );
-
- verifyNever(
- issuesService.createComment(
- slug,
- pr.number!,
- argThat(contains(config.flutterGoldCommentID(pr))),
- ),
- );
- });
-
- test('same commit, checks complete, last status complete', () async {
- // Same commit
- final PullRequest pr = newPullRequest(123, 'abc', 'master');
- prsFromGitHub = <PullRequest>[pr];
- final GithubGoldStatusUpdate status = newStatusUpdate(
- slug,
- pr,
- GithubGoldStatusUpdate.statusCompleted,
- 'abc',
- config.flutterGoldSuccessValue!,
- );
- db.values[status.key] = status;
-
- // Checks complete
- checkRuns = <dynamic>[
- <String, String>{'name': 'framework', 'status': 'completed', 'conclusion': 'success'},
- <String, String>{'name': 'web engine', 'status': 'completed', 'conclusion': 'success'},
- ];
-
- final Body body = await tester.get<Body>(handler);
- expect(body, same(Body.empty));
- expect(status.updates, 0);
- expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty);
- expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty);
-
- // Should not apply labels or make comments
- verifyNever(
- issuesService.addLabelsToIssue(
- slug,
- pr.number!,
- <String>[
- kGoldenFileLabel,
- ],
- ),
- );
-
- verifyNever(
- issuesService.createComment(
- slug,
- pr.number!,
- argThat(contains(config.flutterGoldCommentID(pr))),
- ),
- );
- });
-
- test('same commit, checks complete, last status & gold status is running/awaiting triage, should not comment',
- () async {
- // Same commit
- final PullRequest pr = newPullRequest(123, 'abc', 'master');
- prsFromGitHub = <PullRequest>[pr];
- final GithubGoldStatusUpdate status = newStatusUpdate(
- slug,
- pr,
- GithubGoldStatusUpdate.statusRunning,
- 'abc',
- config.flutterGoldChangesValue!,
- );
- db.values[status.key] = status;
-
- final PullRequest enginePr = newPullRequest(456, 'def', 'main');
- enginePrsFromGitHub = <PullRequest>[enginePr];
- final GithubGoldStatusUpdate engineStatus = newStatusUpdate(
- engineSlug,
- enginePr,
- GithubGoldStatusUpdate.statusRunning,
- 'def',
- config.flutterGoldChangesValue!,
- );
- db.values[engineStatus.key] = engineStatus;
-
- // Checks complete
- checkRuns = <dynamic>[
- <String, String>{'name': 'framework', 'status': 'completed', 'conclusion': 'success'},
- ];
- engineCheckRuns = <dynamic>[
- <String, String>{'name': 'web engine', 'status': 'completed', 'conclusion': 'success'},
- ];
-
- // Gold status is running
- mockHttpClient = MockClient((http.Request request) async {
- if (request.url.toString() ==
- 'https://flutter-gold.skia.org/json/v1/changelist_summary/github/${pr.number}') {
- return http.Response(tryjobDigests(pr), HttpStatus.ok);
- }
- if (request.url.toString() ==
- 'https://flutter-engine-gold.skia.org/json/v1/changelist_summary/github/${enginePr.number}') {
- return http.Response(tryjobDigests(enginePr), HttpStatus.ok);
- }
- throw const HttpException('Unexpected http request');
- });
- handler = PushGoldStatusToGithub(
- config: config,
- authenticationProvider: auth,
- datastoreProvider: (DatastoreDB db) {
- return DatastoreService(
- config.db,
- 5,
- retryOptions: retryOptions,
- );
- },
- goldClient: mockHttpClient,
- ingestionDelay: Duration.zero,
- );
-
- // Already commented for this commit.
- when(issuesService.listCommentsByIssue(slug, pr.number!)).thenAnswer(
- (_) => Stream<IssueComment>.value(
- IssueComment()..body = config.flutterGoldCommentID(pr),
- ),
- );
- when(issuesService.listCommentsByIssue(engineSlug, enginePr.number!)).thenAnswer(
- (_) => Stream<IssueComment>.value(
- IssueComment()..body = config.flutterGoldCommentID(enginePr),
- ),
- );
-
- final Body body = await tester.get<Body>(handler);
- expect(body, same(Body.empty));
- expect(status.updates, 0);
- expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty);
- expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty);
-
- // Should not apply labels or make comments
- verifyNever(
- issuesService.addLabelsToIssue(
- slug,
- pr.number!,
- <String>[
- kGoldenFileLabel,
- ],
- ),
- );
- verifyNever(
- issuesService.addLabelsToIssue(
- engineSlug,
- enginePr.number!,
- <String>[
- kGoldenFileLabel,
- ],
- ),
- );
-
- verifyNever(
- issuesService.createComment(
- slug,
- pr.number!,
- argThat(contains(config.flutterGoldCommentID(pr))),
- ),
- );
- verifyNever(
- issuesService.createComment(
- engineSlug,
- enginePr.number!,
- argThat(contains(config.flutterGoldCommentID(enginePr))),
- ),
- );
- });
-
- test('does nothing for branches not staged to land on main/master', () async {
- // New commit
- final PullRequest pr = newPullRequest(123, 'abc', 'release');
- prsFromGitHub = <PullRequest>[pr];
- final GithubGoldStatusUpdate status = newStatusUpdate(slug, pr, '', '', '');
- db.values[status.key] = status;
-
- // All checks completed
- checkRuns = <dynamic>[
- <String, String>{'name': 'framework', 'status': 'completed', 'conclusion': 'success'},
- ];
-
- final Body body = await tester.get<Body>(handler);
- expect(body, same(Body.empty));
- expect(status.updates, 0);
- expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty);
- expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty);
-
- // Should not apply labels or make comments
- verifyNever(
- issuesService.addLabelsToIssue(
- slug,
- pr.number!,
- <String>[
- kGoldenFileLabel,
- ],
- ),
- );
-
- verifyNever(
- issuesService.createComment(
- slug,
- pr.number!,
- argThat(contains(config.flutterGoldCommentID(pr))),
- ),
- );
- });
-
- test('does not post for draft PRs, does not query Gold', () async {
- // New commit, draft PR
- final PullRequest pr = newPullRequest(123, 'abc', 'master', draft: true);
- prsFromGitHub = <PullRequest>[pr];
- final GithubGoldStatusUpdate status = newStatusUpdate(slug, pr, '', '', '');
- db.values[status.key] = status;
-
- // Checks completed
- checkRuns = <dynamic>[
- <String, String>{'name': 'framework', 'status': 'completed', 'conclusion': 'success'},
- ];
-
- final Body body = await tester.get<Body>(handler);
- expect(body, same(Body.empty));
- expect(status.updates, 0);
- expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty);
- expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty);
-
- // Should not apply labels or make comments
- verifyNever(
- issuesService.addLabelsToIssue(
- slug,
- pr.number!,
- <String>[
- kGoldenFileLabel,
- ],
- ),
- );
-
- verifyNever(
- issuesService.createComment(
- slug,
- pr.number!,
- any,
- ),
- );
- });
-
- test('does not post for draft PRs, does not query Gold', () async {
- // New commit, draft PR
- final PullRequest pr = newPullRequest(123, 'abc', 'master', draft: true);
- prsFromGitHub = <PullRequest>[pr];
- final GithubGoldStatusUpdate status = newStatusUpdate(slug, pr, '', '', '');
- db.values[status.key] = status;
-
- // Checks completed
- checkRuns = <dynamic>[
- <String, String>{'name': 'Linux', 'status': 'completed', 'conclusion': 'success'},
- ];
-
- final Body body = await tester.get<Body>(handler);
- expect(body, same(Body.empty));
- expect(status.updates, 0);
- expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty);
- expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty);
-
- // Should not apply labels or make comments
- verifyNever(
- issuesService.addLabelsToIssue(
- slug,
- pr.number!,
- <String>[
- kGoldenFileLabel,
- ],
- ),
- );
-
- verifyNever(
- issuesService.createComment(
- slug,
- pr.number!,
- any,
- ),
- );
- });
-
- test('does not post for stale PRs, does not query Gold, stale comment', () async {
- // New commit, draft PR
- final PullRequest pr =
- newPullRequest(123, 'abc', 'master', updated: DateTime.now().subtract(const Duration(days: 30)));
- prsFromGitHub = <PullRequest>[pr];
- final GithubGoldStatusUpdate status = newStatusUpdate(slug, pr, '', '', '');
- db.values[status.key] = status;
-
- // Checks completed
- checkRuns = <dynamic>[
- <String, String>{'name': 'framework', 'status': 'completed', 'conclusion': 'success'},
- ];
-
- // Have not already commented for this commit.
- when(issuesService.listCommentsByIssue(slug, pr.number!)).thenAnswer(
- (_) => Stream<IssueComment>.value(
- IssueComment()..body = 'some other comment',
- ),
- );
-
- final Body body = await tester.get<Body>(handler);
- expect(body, same(Body.empty));
- expect(status.updates, 0);
- expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty);
- expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty);
-
- // Should not apply labels, should comment to update
- verifyNever(
- issuesService.addLabelsToIssue(
- slug,
- pr.number!,
- <String>[
- kGoldenFileLabel,
- ],
- ),
- );
-
- verify(
- issuesService.createComment(
- slug,
- pr.number!,
- argThat(contains(config.flutterGoldStalePRValue)),
- ),
- ).called(1);
- });
-
- test('will only comment once on stale PRs', () async {
- // New commit, draft PR
- final PullRequest pr =
- newPullRequest(123, 'abc', 'master', updated: DateTime.now().subtract(const Duration(days: 30)));
- prsFromGitHub = <PullRequest>[pr];
- final GithubGoldStatusUpdate status = newStatusUpdate(slug, pr, '', '', '');
- db.values[status.key] = status;
-
- // Checks completed
- checkRuns = <dynamic>[
- <String, String>{'name': 'framework', 'status': 'completed', 'conclusion': 'success'},
- ];
-
- // Already commented to update.
- when(issuesService.listCommentsByIssue(slug, pr.number!)).thenAnswer(
- (_) => Stream<IssueComment>.value(
- IssueComment()..body = config.flutterGoldStalePRValue,
- ),
- );
-
- final Body body = await tester.get<Body>(handler);
- expect(body, same(Body.empty));
- expect(status.updates, 0);
- expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty);
- expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty);
-
- // Should not apply labels or make comments
- verifyNever(
- issuesService.addLabelsToIssue(
- slug,
- pr.number!,
- <String>[
- kGoldenFileLabel,
- ],
- ),
- );
-
- verifyNever(
- issuesService.createComment(
- slug,
- pr.number!,
- any,
- ),
- );
- });
-
- test('will not fire off stale warning for non-framework PRs', () async {
- // New commit, draft PR
- final PullRequest pr =
- newPullRequest(123, 'abc', 'master', updated: DateTime.now().subtract(const Duration(days: 30)));
- prsFromGitHub = <PullRequest>[pr];
- final GithubGoldStatusUpdate status = newStatusUpdate(slug, pr, '', '', '');
- db.values[status.key] = status;
-
- // Checks completed
- checkRuns = <dynamic>[
- <String, String>{'name': 'tool-test-1', 'status': 'completed', 'conclusion': 'success'},
- ];
-
- // Already commented to update.
- when(issuesService.listCommentsByIssue(slug, pr.number!)).thenAnswer(
- (_) => Stream<IssueComment>.value(
- IssueComment()..body = 'some other comment',
- ),
- );
-
- final Body body = await tester.get<Body>(handler);
- expect(body, same(Body.empty));
- expect(status.updates, 0);
- expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty);
- expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty);
-
- // Should not apply labels or make comments
- verifyNever(
- issuesService.addLabelsToIssue(
- slug,
- pr.number!,
- <String>[
- kGoldenFileLabel,
- ],
- ),
- );
-
- verifyNever(
- issuesService.createComment(
- slug,
- pr.number!,
- any,
- ),
- );
- });
- });
-
- group('updates GitHub and/or Datastore', () {
- test('new commit, checks running', () async {
- // New commit
- final PullRequest flutterPr = newPullRequest(123, 'f-abc', 'master');
- prsFromGitHub = <PullRequest>[flutterPr];
- final GithubGoldStatusUpdate status = newStatusUpdate(slug, flutterPr, '', '', '');
-
- final PullRequest enginePr = newPullRequest(567, 'e-abc', 'main');
- enginePrsFromGitHub = <PullRequest>[enginePr];
- final GithubGoldStatusUpdate engineStatus = newStatusUpdate(engineSlug, enginePr, '', '', '');
-
- db.values[status.key] = status;
- db.values[engineStatus.key] = engineStatus;
-
- // Checks running
- checkRuns = <dynamic>[
- <String, String>{'name': 'framework', 'status': 'in_progress', 'conclusion': 'neutral'},
- ];
- engineCheckRuns = <dynamic>[
- <String, String>{'name': 'web engine', 'status': 'in_progress', 'conclusion': 'neutral'},
- ];
-
- final Body body = await tester.get<Body>(handler);
- expect(body, same(Body.empty));
- expect(status.updates, 1);
- expect(status.status, GithubGoldStatusUpdate.statusRunning);
- expect(engineStatus.updates, 1);
- expect(engineStatus.status, GithubGoldStatusUpdate.statusRunning);
- expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty);
- expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty);
-
- // Should not apply labels or make comments
- verifyNever(
- issuesService.addLabelsToIssue(
- slug,
- flutterPr.number!,
- <String>[
- kGoldenFileLabel,
- ],
- ),
- );
- verifyNever(
- issuesService.addLabelsToIssue(
- engineSlug,
- enginePr.number!,
- <String>[
- kGoldenFileLabel,
- ],
- ),
- );
-
- verifyNever(
- issuesService.createComment(
- slug,
- flutterPr.number!,
- argThat(contains(config.flutterGoldCommentID(flutterPr))),
- ),
- );
- verifyNever(
- issuesService.createComment(
- engineSlug,
- enginePr.number!,
- argThat(contains(config.flutterGoldCommentID(enginePr))),
- ),
- );
- });
-
- test('includes misc test shards', () async {
- // New commit
- final PullRequest pr = newPullRequest(123, 'abc', 'master');
- prsFromGitHub = <PullRequest>[pr];
- final GithubGoldStatusUpdate status = newStatusUpdate(slug, pr, '', '', '');
- db.values[status.key] = status;
-
- // Checks completed
- checkRuns = <dynamic>[
- <String, String>{'name': 'misc', 'status': 'completed', 'conclusion': 'success'},
- ];
-
- // Change detected by Gold
- mockHttpClient = MockClient((http.Request request) async {
- if (request.url.toString() ==
- 'https://flutter-gold.skia.org/json/v1/changelist_summary/github/${pr.number}') {
- return http.Response(tryjobEmpty(), HttpStatus.ok);
- }
- throw const HttpException('Unexpected http request');
- });
- handler = PushGoldStatusToGithub(
- config: config,
- authenticationProvider: auth,
- datastoreProvider: (DatastoreDB db) {
- return DatastoreService(
- config.db,
- 5,
- retryOptions: retryOptions,
- );
- },
- goldClient: mockHttpClient,
- ingestionDelay: Duration.zero,
- );
-
- final Body body = await tester.get<Body>(handler);
- expect(body, same(Body.empty));
- expect(status.updates, 1);
- expect(status.status, GithubGoldStatusUpdate.statusCompleted);
- expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty);
- expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty);
-
- // Should not label or comment
- verifyNever(
- issuesService.addLabelsToIssue(
- slug,
- pr.number!,
- <String>[
- kGoldenFileLabel,
- ],
- ),
- );
-
- verifyNever(
- issuesService.createComment(
- slug,
- pr.number!,
- argThat(contains(config.flutterGoldCommentID(pr))),
- ),
- );
- });
-
- test('new commit, checks complete, no changes detected', () async {
- // New commit
- final PullRequest pr = newPullRequest(123, 'abc', 'master');
- prsFromGitHub = <PullRequest>[pr];
- final GithubGoldStatusUpdate status = newStatusUpdate(slug, pr, '', '', '');
- db.values[status.key] = status;
-
- // Checks completed
- checkRuns = <dynamic>[
- <String, String>{'name': 'framework', 'status': 'completed', 'conclusion': 'success'},
- ];
-
- // Change detected by Gold
- mockHttpClient = MockClient((http.Request request) async {
- if (request.url.toString() ==
- 'https://flutter-gold.skia.org/json/v1/changelist_summary/github/${pr.number}') {
- return http.Response(tryjobEmpty(), HttpStatus.ok);
- }
- throw const HttpException('Unexpected http request');
- });
- handler = PushGoldStatusToGithub(
- config: config,
- authenticationProvider: auth,
- datastoreProvider: (DatastoreDB db) {
- return DatastoreService(
- config.db,
- 5,
- retryOptions: retryOptions,
- );
- },
- goldClient: mockHttpClient,
- ingestionDelay: Duration.zero,
- );
-
- final Body body = await tester.get<Body>(handler);
- expect(body, same(Body.empty));
- expect(status.updates, 1);
- expect(status.status, GithubGoldStatusUpdate.statusCompleted);
- expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty);
- expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty);
-
- // Should not label or comment
- verifyNever(
- issuesService.addLabelsToIssue(
- slug,
- pr.number!,
- <String>[
- kGoldenFileLabel,
- ],
- ),
- );
-
- verifyNever(
- issuesService.createComment(
- slug,
- pr.number!,
- argThat(contains(config.flutterGoldCommentID(pr))),
- ),
- );
- });
-
- test('new commit, checks complete, change detected, should comment', () async {
- // New commit
- final PullRequest pr = newPullRequest(123, 'abc', 'master');
- prsFromGitHub = <PullRequest>[pr];
- final GithubGoldStatusUpdate status = newStatusUpdate(slug, pr, '', '', '');
- db.values[status.key] = status;
-
- // Checks completed
- checkRuns = <dynamic>[
- <String, String>{'name': 'framework', 'status': 'completed', 'conclusion': 'success'},
- ];
-
- // Change detected by Gold
- mockHttpClient = MockClient((http.Request request) async {
- if (request.url.toString() ==
- 'https://flutter-gold.skia.org/json/v1/changelist_summary/github/${pr.number}') {
- return http.Response(tryjobDigests(pr), HttpStatus.ok);
- }
- throw const HttpException('Unexpected http request');
- });
- handler = PushGoldStatusToGithub(
- config: config,
- authenticationProvider: auth,
- datastoreProvider: (DatastoreDB db) {
- return DatastoreService(
- config.db,
- 5,
- retryOptions: retryOptions,
- );
- },
- goldClient: mockHttpClient,
- ingestionDelay: Duration.zero,
- );
-
- // Have not already commented for this commit.
- when(issuesService.listCommentsByIssue(slug, pr.number!)).thenAnswer(
- (_) => Stream<IssueComment>.value(
- IssueComment()..body = 'some other comment',
- ),
- );
-
- final Body body = await tester.get<Body>(handler);
- expect(body, same(Body.empty));
- expect(status.updates, 1);
- expect(status.status, GithubGoldStatusUpdate.statusRunning);
- expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty);
- expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty);
-
- // Should label and comment
- verify(
- issuesService.addLabelsToIssue(
- slug,
- pr.number!,
- <String>[
- kGoldenFileLabel,
- ],
- ),
- ).called(1);
-
- verify(
- issuesService.createComment(
- slug,
- pr.number!,
- argThat(contains(config.flutterGoldCommentID(pr))),
- ),
- ).called(1);
- });
-
- test('same commit, checks complete, last status was waiting & gold status is needing triage, should comment',
- () async {
- // Same commit
- final PullRequest pr = newPullRequest(123, 'abc', 'master');
- prsFromGitHub = <PullRequest>[pr];
- final GithubGoldStatusUpdate status =
- newStatusUpdate(slug, pr, GithubGoldStatusUpdate.statusRunning, 'abc', config.flutterGoldPendingValue!);
- db.values[status.key] = status;
-
- // Checks complete
- checkRuns = <dynamic>[
- <String, String>{'name': 'framework', 'status': 'completed', 'conclusion': 'success'},
- ];
-
- // Gold status is running
- mockHttpClient = MockClient((http.Request request) async {
- if (request.url.toString() ==
- 'https://flutter-gold.skia.org/json/v1/changelist_summary/github/${pr.number}') {
- return http.Response(tryjobDigests(pr), HttpStatus.ok);
- }
- throw const HttpException('Unexpected http request');
- });
- handler = PushGoldStatusToGithub(
- config: config,
- authenticationProvider: auth,
- datastoreProvider: (DatastoreDB db) {
- return DatastoreService(
- config.db,
- 5,
- retryOptions: retryOptions,
- );
- },
- goldClient: mockHttpClient,
- ingestionDelay: Duration.zero,
- );
-
- // Have not already commented for this commit.
- when(issuesService.listCommentsByIssue(slug, pr.number!)).thenAnswer(
- (_) => Stream<IssueComment>.value(
- IssueComment()..body = 'some other comment',
- ),
- );
-
- final Body body = await tester.get<Body>(handler);
- expect(body, same(Body.empty));
- expect(status.updates, 1);
- expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty);
- expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty);
-
- // Should apply labels and make comment
- verify(
- issuesService.addLabelsToIssue(
- slug,
- pr.number!,
- <String>[
- kGoldenFileLabel,
- ],
- ),
- ).called(1);
-
- verify(
- issuesService.createComment(
- slug,
- pr.number!,
- argThat(contains(config.flutterGoldCommentID(pr))),
- ),
- ).called(1);
- });
-
- test('uses shorter comment after first comment to reduce noise', () async {
- // Same commit
- final PullRequest pr = newPullRequest(123, 'abc', 'master');
- prsFromGitHub = <PullRequest>[pr];
- final GithubGoldStatusUpdate status =
- newStatusUpdate(slug, pr, GithubGoldStatusUpdate.statusRunning, 'abc', config.flutterGoldPendingValue!);
- db.values[status.key] = status;
-
- // Checks complete
- checkRuns = <dynamic>[
- <String, String>{'name': 'framework', 'completed': 'in_progress', 'conclusion': 'success'},
- ];
-
- // Gold status is running
- mockHttpClient = MockClient((http.Request request) async {
- if (request.url.toString() ==
- 'https://flutter-gold.skia.org/json/v1/changelist_summary/github/${pr.number}') {
- return http.Response(tryjobDigests(pr), HttpStatus.ok);
- }
- throw const HttpException('Unexpected http request');
- });
- handler = PushGoldStatusToGithub(
- config: config,
- authenticationProvider: auth,
- datastoreProvider: (DatastoreDB db) {
- return DatastoreService(
- config.db,
- 5,
- retryOptions: retryOptions,
- );
- },
- goldClient: mockHttpClient,
- ingestionDelay: Duration.zero,
- );
-
- // Have not already commented for this commit.
- when(issuesService.listCommentsByIssue(slug, pr.number!)).thenAnswer(
- (_) => Stream<IssueComment>.value(
- IssueComment()..body = config.flutterGoldInitialAlertValue,
- ),
- );
-
- final Body body = await tester.get<Body>(handler);
- expect(body, same(Body.empty));
- expect(status.updates, 1);
- expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty);
- expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty);
-
- // Should apply labels and make comment
- verify(
- issuesService.addLabelsToIssue(
- slug,
- pr.number!,
- <String>[
- kGoldenFileLabel,
- ],
- ),
- ).called(1);
-
- verify(
- issuesService.createComment(
- slug,
- pr.number!,
- argThat(contains(config.flutterGoldFollowUpAlertValue)),
- ),
- ).called(1);
- verifyNever(
- issuesService.createComment(
- slug,
- pr.number!,
- argThat(contains(config.flutterGoldInitialAlertValue)),
- ),
- );
- verifyNever(
- issuesService.createComment(
- slug,
- pr.number!,
- argThat(contains(config.flutterGoldAlertConstantValue)),
- ),
- );
- });
-
- test('same commit, checks complete, new status, should not comment', () async {
- // Same commit: abc
- final PullRequest pr = newPullRequest(123, 'abc', 'master');
- prsFromGitHub = <PullRequest>[pr];
- final GithubGoldStatusUpdate status =
- newStatusUpdate(slug, pr, GithubGoldStatusUpdate.statusRunning, 'abc', config.flutterGoldPendingValue!);
- db.values[status.key] = status;
-
- // Checks completed
- checkRuns = <dynamic>[
- <String, String>{'name': 'framework', 'status': 'completed', 'conclusion': 'success'},
- ];
-
- // New status: completed/triaged/no changes
- mockHttpClient = MockClient((http.Request request) async {
- if (request.url.toString() ==
- 'https://flutter-gold.skia.org/json/v1/changelist_summary/github/${pr.number}') {
- return http.Response(tryjobEmpty(), HttpStatus.ok);
- }
- throw const HttpException('Unexpected http request');
- });
- handler = PushGoldStatusToGithub(
- config: config,
- authenticationProvider: auth,
- datastoreProvider: (DatastoreDB db) {
- return DatastoreService(
- config.db,
- 5,
- retryOptions: retryOptions,
- );
- },
- goldClient: mockHttpClient,
- ingestionDelay: Duration.zero,
- );
-
- when(issuesService.listCommentsByIssue(slug, pr.number!)).thenAnswer(
- (_) => Stream<IssueComment>.value(
- IssueComment()..body = 'some other comment',
- ),
- );
-
- final Body body = await tester.get<Body>(handler);
- expect(body, same(Body.empty));
- expect(status.updates, 1);
- expect(status.status, GithubGoldStatusUpdate.statusCompleted);
- expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty);
- expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty);
-
- // Should not label or comment
- verifyNever(
- issuesService.addLabelsToIssue(
- slug,
- pr.number!,
- <String>[
- kGoldenFileLabel,
- ],
- ),
- );
-
- verifyNever(
- issuesService.createComment(
- slug,
- pr.number!,
- argThat(contains(config.flutterGoldCommentID(pr))),
- ),
- );
- });
-
- test('will inform contributor of unresolved check for ATF draft status', () async {
- // New commit, draft PR
- final PullRequest pr = newPullRequest(123, 'abc', 'master', draft: true);
- prsFromGitHub = <PullRequest>[pr];
- final GithubGoldStatusUpdate status =
- newStatusUpdate(slug, pr, GithubGoldStatusUpdate.statusRunning, 'abc', config.flutterGoldPendingValue!);
- db.values[status.key] = status;
-
- // Checks completed
- checkRuns = <dynamic>[
- <String, String>{'name': 'framework', 'status': 'completed', 'conclusion': 'success'},
- ];
- when(issuesService.listCommentsByIssue(slug, pr.number!)).thenAnswer(
- (_) => Stream<IssueComment>.value(
- IssueComment()..body = 'some other comment',
- ),
- );
-
- final Body body = await tester.get<Body>(handler);
- expect(body, same(Body.empty));
- expect(status.updates, 0);
- expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty);
- expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty);
-
- // Should not apply labels or make comments
- verifyNever(
- issuesService.addLabelsToIssue(
- slug,
- pr.number!,
- <String>[
- kGoldenFileLabel,
- ],
- ),
- );
-
- verify(
- issuesService.createComment(
- slug,
- pr.number!,
- argThat(contains(config.flutterGoldDraftChangeValue)),
- ),
- ).called(1);
- });
-
- test('will only inform contributor of unresolved check for ATF draft status once', () async {
- // New commit, draft PR
- final PullRequest pr = newPullRequest(123, 'abc', 'master', draft: true);
- prsFromGitHub = <PullRequest>[pr];
- final GithubGoldStatusUpdate status =
- newStatusUpdate(slug, pr, GithubGoldStatusUpdate.statusRunning, 'abc', config.flutterGoldPendingValue!);
- db.values[status.key] = status;
-
- // Checks completed
- checkRuns = <dynamic>[
- <String, String>{'name': 'framework', 'status': 'completed', 'conclusion': 'success'},
- ];
- when(issuesService.listCommentsByIssue(slug, pr.number!)).thenAnswer(
- (_) => Stream<IssueComment>.value(
- IssueComment()..body = config.flutterGoldDraftChangeValue,
- ),
- );
-
- final Body body = await tester.get<Body>(handler);
- expect(body, same(Body.empty));
- expect(status.updates, 0);
- expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty);
- expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty);
-
- // Should not apply labels or make comments
- verifyNever(
- issuesService.addLabelsToIssue(
- slug,
- pr.number!,
- <String>[
- kGoldenFileLabel,
- ],
- ),
- );
-
- verifyNever(
- issuesService.createComment(
- slug,
- pr.number!,
- argThat(contains(config.flutterGoldDraftChangeValue)),
- ),
- );
- });
-
- test('delivers pending state for failing checks, does not query Gold', () async {
- // New commit
- final PullRequest pr = newPullRequest(123, 'abc', 'master');
- prsFromGitHub = <PullRequest>[pr];
- final GithubGoldStatusUpdate status = newStatusUpdate(slug, pr, '', '', '');
- db.values[status.key] = status;
-
- // Checks failed
- checkRuns = <dynamic>[
- <String, String>{'name': 'framework', 'status': 'completed', 'conclusion': 'failure'},
- ];
-
- final Body body = await tester.get<Body>(handler);
- expect(body, same(Body.empty));
- expect(status.updates, 1);
- expect(status.status, GithubGoldStatusUpdate.statusRunning);
- expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty);
- expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty);
-
- // Should not apply labels or make comments
- verifyNever(
- issuesService.addLabelsToIssue(
- slug,
- pr.number!,
- <String>[
- kGoldenFileLabel,
- ],
- ),
- );
-
- verifyNever(
- issuesService.createComment(
- slug,
- pr.number!,
- argThat(contains(config.flutterGoldCommentID(pr))),
- ),
- );
- });
- });
-
- test('Completed pull request does not skip follow-up prs with early return', () async {
- final PullRequest completedPR = newPullRequest(123, 'abc', 'master');
- final PullRequest followUpPR = newPullRequest(456, 'def', 'master');
- prsFromGitHub = <PullRequest>[
- completedPR,
- followUpPR,
- ];
- final GithubGoldStatusUpdate completedStatus = newStatusUpdate(
- slug,
- completedPR,
- GithubGoldStatusUpdate.statusCompleted,
- 'abc',
- config.flutterGoldSuccessValue!,
- );
- final GithubGoldStatusUpdate followUpStatus = newStatusUpdate(slug, followUpPR, '', '', '');
- db.values[completedStatus.key] = completedStatus;
- db.values[followUpStatus.key] = followUpStatus;
-
- // Checks completed
- checkRuns = <dynamic>[
- <String, String>{'name': 'framework', 'status': 'completed', 'conclusion': 'success'},
- ];
-
- // New status: completed/triaged/no changes
- mockHttpClient = MockClient((http.Request request) async {
- if (request.url.toString() ==
- 'https://flutter-gold.skia.org/json/v1/changelist_summary/github/${completedPR.number}') {
- return http.Response(tryjobEmpty(), HttpStatus.ok);
- }
- if (request.url.toString() ==
- 'https://flutter-gold.skia.org/json/v1/changelist_summary/github/${followUpPR.number}') {
- return http.Response(tryjobEmpty(), HttpStatus.ok);
- }
- throw const HttpException('Unexpected http request');
- });
- handler = PushGoldStatusToGithub(
- config: config,
- authenticationProvider: auth,
- datastoreProvider: (DatastoreDB db) {
- return DatastoreService(
- config.db,
- 5,
- retryOptions: retryOptions,
- );
- },
- goldClient: mockHttpClient,
- ingestionDelay: Duration.zero,
- );
-
- when(issuesService.listCommentsByIssue(slug, completedPR.number!)).thenAnswer(
- (_) => Stream<IssueComment>.value(
- IssueComment()..body = 'some other comment',
- ),
- );
-
- final Body body = await tester.get<Body>(handler);
- expect(body, same(Body.empty));
- expect(completedStatus.updates, 0);
- expect(followUpStatus.updates, 1);
- expect(completedStatus.status, GithubGoldStatusUpdate.statusCompleted);
- expect(followUpStatus.status, GithubGoldStatusUpdate.statusCompleted);
- expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty);
- expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty);
- });
-
- test('accounts for null status description when parsing for Luci builds', () async {
- // Same commit
- final PullRequest pr = newPullRequest(123, 'abc', 'master');
- prsFromGitHub = <PullRequest>[pr];
- final GithubGoldStatusUpdate status = newStatusUpdate(
- slug,
- pr,
- GithubGoldStatusUpdate.statusRunning,
- 'abc',
- config.flutterGoldPendingValue!,
- );
- db.values[status.key] = status;
-
- // null status for luci build
- checkRuns = <dynamic>[
- <String, String?>{
- 'name': 'framework',
- 'status': null,
- 'conclusion': null,
- }
- ];
-
- final Body body = await tester.get<Body>(handler);
- expect(body, same(Body.empty));
- expect(status.updates, 0);
- expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty);
- expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty);
-
- // Should not apply labels or make comments
- verifyNever(
- issuesService.addLabelsToIssue(
- slug,
- pr.number!,
- <String>[
- kGoldenFileLabel,
- ],
- ),
- );
-
- verifyNever(
- issuesService.createComment(
- slug,
- pr.number!,
- argThat(contains(config.flutterGoldCommentID(pr))),
- ),
- );
- });
-
- test('uses the correct Gold endpoint to get status', () async {
- // New commit
- final PullRequest pr = newPullRequest(123, 'abc', 'master');
- prsFromGitHub = <PullRequest>[pr];
- final GithubGoldStatusUpdate status = newStatusUpdate(slug, pr, '', '', '');
- db.values[status.key] = status;
-
- final PullRequest enginePr = newPullRequest(456, 'def', 'main');
- enginePrsFromGitHub = <PullRequest>[enginePr];
- final GithubGoldStatusUpdate engineStatus = newStatusUpdate(engineSlug, enginePr, '', '', '');
- db.values[engineStatus.key] = engineStatus;
-
- // Checks completed
- checkRuns = <dynamic>[
- <String, String>{'name': 'framework', 'status': 'completed', 'conclusion': 'success'},
- ];
- engineCheckRuns = <dynamic>[
- <String, String>{'name': 'web engine', 'status': 'completed', 'conclusion': 'success'},
- ];
-
- // Requests sent to Gold.
- final List<String> goldRequests = <String>[];
- mockHttpClient = MockClient((http.Request request) async {
- final String requestUrl = request.url.toString();
- goldRequests.add(requestUrl);
-
- final int prNumber = int.parse(requestUrl.split('/').last);
- final PullRequest requestedPr;
- if (prNumber == pr.number) {
- requestedPr = pr;
- } else if (prNumber == enginePr.number) {
- requestedPr = enginePr;
- } else {
- throw HttpException('Unexpected http request for PR#$prNumber');
- }
- return http.Response(tryjobDigests(requestedPr), HttpStatus.ok);
- });
- handler = PushGoldStatusToGithub(
- config: config,
- authenticationProvider: auth,
- datastoreProvider: (DatastoreDB db) {
- return DatastoreService(
- config.db,
- 5,
- retryOptions: retryOptions,
- );
- },
- goldClient: mockHttpClient,
- ingestionDelay: Duration.zero,
- );
-
- // Have not already commented for this commit.
- when(issuesService.listCommentsByIssue(slug, pr.number!)).thenAnswer(
- (_) => Stream<IssueComment>.value(
- IssueComment()..body = 'some other comment',
- ),
- );
- when(issuesService.listCommentsByIssue(engineSlug, enginePr.number!)).thenAnswer(
- (_) => Stream<IssueComment>.value(
- IssueComment()..body = 'some other comment',
- ),
- );
-
- await tester.get<Body>(handler);
-
- expect(goldRequests, <String>[
- 'https://flutter-gold.skia.org/json/v1/changelist_summary/github/${pr.number}',
- 'https://flutter-engine-gold.skia.org/json/v1/changelist_summary/github/${enginePr.number}',
- ]);
- });
- });
- });
-}
-
-QueryResult createGithubQueryResult(List<dynamic> statuses) {
- return createFakeQueryResult(
- data: <String, dynamic>{
- 'repository': <String, dynamic>{
- 'pullRequest': <String, dynamic>{
- 'commits': <String, dynamic>{
- 'nodes': <dynamic>[
- <String, dynamic>{
- 'commit': <String, dynamic>{
- 'checkSuites': <String, dynamic>{
- 'nodes': <dynamic>[
- <String, dynamic>{
- 'checkRuns': <String, dynamic>{'nodes': statuses},
- }
- ],
- },
- },
- }
- ],
- },
- },
- },
- },
- );
-}
-
-/// JSON response template for Skia Gold empty tryjob status request.
-String tryjobEmpty() {
- return '''
- {
- "changelist_id": "123",
- "patchsets": [
- {
- "new_images": 0,
- "new_untriaged_images": 0,
- "total_untriaged_images": 0,
- "patchset_id": "abc",
- "patchset_order": 1
- }
- ],
- "outdated": false
- }
- ''';
-}
-
-/// JSON response template for Skia Gold untriaged tryjob status request.
-String tryjobDigests(PullRequest pr) {
- return '''
- {
- "changelist_id": "${pr.number!}",
- "patchsets": [
- {
- "new_images": 1,
- "new_untriaged_images": 1,
- "total_untriaged_images": 1,
- "patchset_id": "${pr.head!.sha!}",
- "patchset_order": 1
- }
- ],
- "outdated": false
- }
- ''';
-}
diff --git a/app_dart/test/request_handlers/reset_prod_task_test.dart b/app_dart/test/request_handlers/reset_prod_task_test.dart
deleted file mode 100644
index 6be487a..0000000
--- a/app_dart/test/request_handlers/reset_prod_task_test.dart
+++ /dev/null
@@ -1,188 +0,0 @@
-// Copyright 2020 The Flutter 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 'package:cocoon_service/cocoon_service.dart';
-import 'package:cocoon_service/src/model/appengine/commit.dart';
-import 'package:cocoon_service/src/model/appengine/task.dart';
-import 'package:cocoon_service/src/request_handling/exceptions.dart';
-import 'package:mockito/mockito.dart';
-import 'package:test/test.dart';
-
-import '../src/datastore/fake_config.dart';
-import '../src/datastore/fake_datastore.dart';
-import '../src/request_handling/api_request_handler_tester.dart';
-import '../src/request_handling/fake_authentication.dart';
-import '../src/service/fake_scheduler.dart';
-import '../src/utilities/entity_generators.dart';
-import '../src/utilities/mocks.dart';
-
-void main() {
- group('ResetProdTask', () {
- FakeClientContext clientContext;
- late ResetProdTask handler;
- late FakeConfig config;
- FakeKeyHelper keyHelper;
- late MockLuciBuildService mockLuciBuildService;
- late ApiRequestHandlerTester tester;
- late Commit commit;
- late Task task;
-
- setUp(() {
- final FakeDatastoreDB datastoreDB = FakeDatastoreDB();
- clientContext = FakeClientContext();
- clientContext.isDevelopmentEnvironment = false;
- keyHelper = FakeKeyHelper(applicationContext: clientContext.applicationContext);
- config = FakeConfig(
- dbValue: datastoreDB,
- keyHelperValue: keyHelper,
- supportedBranchesValue: <String>[
- Config.defaultBranch(Config.flutterSlug),
- ],
- );
- final FakeAuthenticatedContext authContext = FakeAuthenticatedContext(clientContext: clientContext);
- tester = ApiRequestHandlerTester(context: authContext);
- mockLuciBuildService = MockLuciBuildService();
- handler = ResetProdTask(
- config: config,
- authenticationProvider: FakeAuthenticationProvider(clientContext: clientContext),
- luciBuildService: mockLuciBuildService,
- scheduler: FakeScheduler(
- config: config,
- ciYaml: exampleConfig,
- ),
- );
- commit = generateCommit(1);
- task = generateTask(
- 1,
- name: 'Linux A',
- parent: commit,
- status: Task.statusFailed,
- );
- tester.requestData = <String, dynamic>{
- 'Key': config.keyHelper.encode(task.key),
- };
-
- when(
- mockLuciBuildService.checkRerunBuilder(
- commit: anyNamed('commit'),
- datastore: anyNamed('datastore'),
- task: anyNamed('task'),
- target: anyNamed('target'),
- tags: anyNamed('tags'),
- ignoreChecks: anyNamed('ignoreChecks'),
- ),
- ).thenAnswer((_) async => true);
- });
- test('Schedule new task', () async {
- config.db.values[task.key] = task;
- config.db.values[commit.key] = commit;
- expect(await tester.post(handler), Body.empty);
- });
-
- test('Re-schedule existing task', () async {
- config.db.values[task.key] = task;
- config.db.values[commit.key] = commit;
- expect(await tester.post(handler), Body.empty);
- });
-
- test('Re-schedule passing all the parameters', () async {
- config.db.values[task.key] = task;
- config.db.values[commit.key] = commit;
- tester.requestData = <String, dynamic>{
- 'Commit': commit.sha,
- 'Task': task.name,
- 'Repo': commit.slug.name,
- };
- expect(await tester.post(handler), Body.empty);
- verify(
- mockLuciBuildService.checkRerunBuilder(
- commit: anyNamed('commit'),
- datastore: anyNamed('datastore'),
- task: anyNamed('task'),
- target: anyNamed('target'),
- tags: anyNamed('tags'),
- ignoreChecks: true,
- ),
- ).called(1);
- });
-
- test('Rerun all failed tasks when task name is all', () async {
- final Task taskA = generateTask(2, name: 'Linux A', parent: commit, status: Task.statusFailed);
- final Task taskB = generateTask(3, name: 'Mac A', parent: commit, status: Task.statusFailed);
- config.db.values[taskA.key] = taskA;
- config.db.values[taskB.key] = taskB;
- config.db.values[commit.key] = commit;
- tester.requestData = <String, dynamic>{
- 'Commit': commit.sha,
- 'Task': 'all',
- 'Repo': commit.slug.name,
- };
- expect(await tester.post(handler), Body.empty);
- verify(
- mockLuciBuildService.checkRerunBuilder(
- commit: anyNamed('commit'),
- datastore: anyNamed('datastore'),
- task: anyNamed('task'),
- target: anyNamed('target'),
- tags: anyNamed('tags'),
- ignoreChecks: false,
- ),
- ).called(2);
- });
-
- test('Rerun all runs nothing when everything is passed', () async {
- final Task task = generateTask(2, name: 'Windows A', parent: commit, status: Task.statusSucceeded);
- config.db.values[task.key] = task;
- config.db.values[commit.key] = commit;
- tester.requestData = <String, dynamic>{
- 'Commit': commit.sha,
- 'Task': 'all',
- 'Repo': commit.slug.name,
- };
- expect(await tester.post(handler), Body.empty);
- verifyNever(
- mockLuciBuildService.checkRerunBuilder(
- commit: anyNamed('commit'),
- datastore: anyNamed('datastore'),
- task: anyNamed('task'),
- target: anyNamed('target'),
- tags: anyNamed('tags'),
- ignoreChecks: false,
- ),
- );
- });
-
- test('Re-schedule without any parameters raises exception', () async {
- tester.requestData = <String, dynamic>{};
- expect(() => tester.post(handler), throwsA(isA<BadRequestException>()));
- });
-
- test('Re-schedule existing task even though taskName is missing in the task', () async {
- config.db.values[task.key] = task;
- config.db.values[commit.key] = commit;
- expect(await tester.post(handler), Body.empty);
- });
-
- test('Fails if task is not rerun', () async {
- when(
- mockLuciBuildService.checkRerunBuilder(
- commit: anyNamed('commit'),
- datastore: anyNamed('datastore'),
- task: anyNamed('task'),
- target: anyNamed('target'),
- tags: anyNamed('tags'),
- ignoreChecks: true,
- ),
- ).thenAnswer((_) async => false);
- config.db.values[task.key] = task;
- config.db.values[commit.key] = commit;
- expect(() => tester.post(handler), throwsA(isA<InternalServerError>()));
- });
-
- test('Fails if commit does not exist', () async {
- config.db.values[task.key] = task;
- expect(() => tester.post(handler), throwsA(isA<StateError>()));
- });
- });
-}
diff --git a/app_dart/test/request_handlers/reset_try_task_test.dart b/app_dart/test/request_handlers/reset_try_task_test.dart
deleted file mode 100644
index 5a6adbf..0000000
--- a/app_dart/test/request_handlers/reset_try_task_test.dart
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright 2020 The Flutter 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 'package:cocoon_service/src/request_handlers/reset_try_task.dart';
-import 'package:cocoon_service/src/request_handling/body.dart';
-import 'package:cocoon_service/src/request_handling/exceptions.dart';
-import 'package:github/github.dart';
-import 'package:mockito/mockito.dart';
-import 'package:test/test.dart';
-
-import '../src/datastore/fake_config.dart';
-import '../src/request_handling/api_request_handler_tester.dart';
-import '../src/request_handling/fake_authentication.dart';
-import '../src/request_handling/fake_http.dart';
-import '../src/service/fake_github_service.dart';
-import '../src/service/fake_scheduler.dart';
-import '../src/utilities/entity_generators.dart';
-import '../src/utilities/mocks.dart';
-
-void main() {
- group('ResetTryTask', () {
- late ApiRequestHandlerTester tester;
- FakeClientContext clientContext;
- late ResetTryTask handler;
- late FakeConfig config;
- FakeScheduler fakeScheduler;
- FakeAuthenticatedContext authContext;
- MockGitHub mockGithub;
- MockPullRequestsService mockPullRequestsService;
- late MockGithubChecksUtil mockGithubChecksUtil;
-
- setUp(() {
- clientContext = FakeClientContext();
- clientContext.isDevelopmentEnvironment = false;
- authContext = FakeAuthenticatedContext(clientContext: clientContext);
- mockGithub = MockGitHub();
- mockPullRequestsService = MockPullRequestsService();
- config = FakeConfig(githubClient: mockGithub, githubService: FakeGithubService());
- mockGithubChecksUtil = MockGithubChecksUtil();
- tester = ApiRequestHandlerTester(context: authContext);
- fakeScheduler = FakeScheduler(
- config: config,
- githubChecksUtil: mockGithubChecksUtil,
- );
- handler = ResetTryTask(
- config: config,
- authenticationProvider: FakeAuthenticationProvider(clientContext: clientContext),
- scheduler: fakeScheduler,
- );
- when(mockGithub.pullRequests).thenReturn(mockPullRequestsService);
- when(mockPullRequestsService.get(any, 123)).thenAnswer((_) async => generatePullRequest(id: 123));
- });
-
- test('Empty repo', () async {
- tester.request = FakeHttpRequest(
- queryParametersValue: <String, String>{
- 'pr': '123',
- },
- );
- expect(() => tester.get(handler), throwsA(isA<BadRequestException>()));
- });
-
- test('Empty pr', () async {
- tester.request = FakeHttpRequest(
- queryParametersValue: <String, String>{
- 'repo': 'flutter',
- },
- );
- expect(() => tester.get(handler), throwsA(isA<BadRequestException>()));
- });
-
- test('Trigger builds if all parameters are correct', () async {
- when(mockGithubChecksUtil.createCheckRun(any, any, any, any, output: anyNamed('output'))).thenAnswer((_) async {
- return CheckRun.fromJson(const <String, dynamic>{
- 'id': 1,
- 'started_at': '2020-05-10T02:49:31Z',
- 'check_suite': <String, dynamic>{'id': 2},
- });
- });
- tester.request = FakeHttpRequest(
- queryParametersValue: <String, String>{
- ResetTryTask.kRepoParam: 'flutter',
- ResetTryTask.kPullRequestNumberParam: '123',
- },
- );
- expect(await tester.get(handler), Body.empty);
- });
-
- test('Parses empty builder correctly', () {
- final List<String> builders = handler.getBuilderList('');
- expect(builders.isEmpty, true);
- });
-
- test('Parses non-empty builder correctly', () {
- expect(handler.getBuilderList('a, b, c'), <String>['a', 'b', 'c']);
- });
- });
-}
diff --git a/app_dart/test/request_handlers/scheduler/batch_backfiller_test.dart b/app_dart/test/request_handlers/scheduler/batch_backfiller_test.dart
deleted file mode 100644
index 7f81f5f..0000000
--- a/app_dart/test/request_handlers/scheduler/batch_backfiller_test.dart
+++ /dev/null
@@ -1,284 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:cocoon_service/cocoon_service.dart';
-import 'package:cocoon_service/src/model/appengine/commit.dart';
-import 'package:cocoon_service/src/model/appengine/task.dart';
-import 'package:cocoon_service/src/model/ci_yaml/target.dart';
-import 'package:cocoon_service/src/model/luci/buildbucket.dart';
-import 'package:mockito/mockito.dart';
-import 'package:test/test.dart';
-
-import '../../src/datastore/fake_config.dart';
-import '../../src/datastore/fake_datastore.dart';
-import '../../src/request_handling/fake_pubsub.dart';
-import '../../src/request_handling/request_handler_tester.dart';
-import '../../src/service/fake_luci_build_service.dart';
-import '../../src/service/fake_scheduler.dart';
-import '../../src/utilities/entity_generators.dart';
-import '../../src/utilities/mocks.dart';
-
-final List<Commit> commits = <Commit>[
- generateCommit(3),
- generateCommit(2),
- generateCommit(1),
-];
-
-void main() {
- late BatchBackfiller handler;
- late RequestHandlerTester tester;
- late FakeDatastoreDB db;
- late FakePubSub pubsub;
- late FakeScheduler scheduler;
- late MockGithubChecksUtil mockGithubChecksUtil;
- late Config config;
-
- group('BatchBackfiller', () {
- setUp(() async {
- db = FakeDatastoreDB()..addOnQuery<Commit>((Iterable<Commit> results) => commits);
- config = FakeConfig(dbValue: db, backfillerTargetLimitValue: 2);
- pubsub = FakePubSub();
- mockGithubChecksUtil = MockGithubChecksUtil();
- when(
- mockGithubChecksUtil.createCheckRun(
- any,
- any,
- any,
- any,
- output: anyNamed('output'),
- ),
- ).thenAnswer((_) async => generateCheckRun(1));
- scheduler = FakeScheduler(
- config: config,
- ciYaml: batchPolicyConfig,
- githubChecksUtil: mockGithubChecksUtil,
- luciBuildService: FakeLuciBuildService(
- config: config,
- pubsub: pubsub,
- githubChecksUtil: mockGithubChecksUtil,
- ),
- );
- handler = BatchBackfiller(
- config: config,
- scheduler: scheduler,
- );
- tester = RequestHandlerTester();
- });
-
- test('does not backfill on completed task column', () async {
- final List<Task> allGreen = <Task>[
- generateTask(1, name: 'Linux_android A', status: Task.statusSucceeded),
- generateTask(2, name: 'Linux_android A', status: Task.statusSucceeded),
- generateTask(3, name: 'Linux_android A', status: Task.statusSucceeded),
- ];
- db.addOnQuery<Task>((Iterable<Task> results) => allGreen);
- await tester.get(handler);
- expect(pubsub.messages, isEmpty);
- });
-
- test('does not backfill when there is a running task', () async {
- final List<Task> middleTaskInProgress = <Task>[
- generateTask(1, name: 'Linux_android A', status: Task.statusNew),
- generateTask(2, name: 'Linux_android A', status: Task.statusInProgress),
- generateTask(3, name: 'Linux_android A', status: Task.statusNew),
- ];
- db.addOnQuery<Task>((Iterable<Task> results) => middleTaskInProgress);
- await tester.get(handler);
- expect(pubsub.messages, isEmpty);
- });
-
- test('does not backfill when task does not exist in TOT', () async {
- scheduler = FakeScheduler(
- config: config,
- ciYaml: notInToTConfig,
- githubChecksUtil: mockGithubChecksUtil,
- luciBuildService: FakeLuciBuildService(
- config: config,
- pubsub: pubsub,
- githubChecksUtil: mockGithubChecksUtil,
- ),
- );
- handler = BatchBackfiller(
- config: config,
- scheduler: scheduler,
- );
- final List<Task> allGray = <Task>[
- generateTask(1, name: 'Linux_android B', status: Task.statusNew),
- ];
- db.addOnQuery<Task>((Iterable<Task> results) => allGray);
- await tester.get(handler);
- expect(pubsub.messages.length, 0);
- });
-
- test('backfills latest task', () async {
- final List<Task> allGray = <Task>[
- generateTask(1, name: 'Linux_android A', status: Task.statusNew),
- generateTask(2, name: 'Linux_android A', status: Task.statusNew),
- generateTask(3, name: 'Linux_android A', status: Task.statusNew),
- ];
- db.addOnQuery<Task>((Iterable<Task> results) => allGray);
- await tester.get(handler);
- expect(pubsub.messages.length, 1);
- final ScheduleBuildRequest scheduleBuildRequest =
- (pubsub.messages.single as BatchRequest).requests!.single.scheduleBuild!;
- expect(scheduleBuildRequest.priority, LuciBuildService.kBackfillPriority);
- });
-
- test('does not backfill targets when number of available tasks is less than BatchPolicy.kBatchSize', () async {
- final List<Task> scheduleA = <Task>[
- // Linux_android A
- generateTask(1, name: 'Linux_android A', status: Task.statusNew),
- ];
- db.addOnQuery<Task>((Iterable<Task> results) => scheduleA);
- await tester.get(handler);
- expect(pubsub.messages.length, 0);
- });
-
- test('backfills earlier failed task with higher priority', () async {
- final List<Task> allGray = <Task>[
- generateTask(1, name: 'Linux_android A', status: Task.statusNew),
- generateTask(2, name: 'Linux_android A', status: Task.statusNew),
- generateTask(3, name: 'Linux_android A', status: Task.statusFailed),
- ];
- db.addOnQuery<Task>((Iterable<Task> results) => allGray);
- await tester.get(handler);
- expect(pubsub.messages.length, 1);
- final ScheduleBuildRequest scheduleBuildRequest =
- (pubsub.messages.single as BatchRequest).requests!.single.scheduleBuild!;
- expect(scheduleBuildRequest.priority, LuciBuildService.kRerunPriority);
- });
-
- test('backfills task successfully with retry', () async {
- pubsub.exceptionFlag = true;
- pubsub.exceptionRepetition = 1;
- final List<Task> allGray = <Task>[
- generateTask(1, name: 'Linux_android A', status: Task.statusNew),
- generateTask(2, name: 'Linux_android A', status: Task.statusNew),
- generateTask(3, name: 'Linux_android A', status: Task.statusFailed),
- ];
- db.addOnQuery<Task>((Iterable<Task> results) => allGray);
- await tester.get(handler);
- expect(pubsub.messages.length, 1);
- final ScheduleBuildRequest scheduleBuildRequest =
- (pubsub.messages.single as BatchRequest).requests!.single.scheduleBuild!;
- expect(scheduleBuildRequest.priority, LuciBuildService.kRerunPriority);
- });
-
- test('fails to backfill tasks when retry limit is hit', () async {
- pubsub.exceptionFlag = true;
- pubsub.exceptionRepetition = 3;
- final List<Task> allGray = <Task>[
- generateTask(1, name: 'Linux_android A', status: Task.statusNew),
- generateTask(2, name: 'Linux_android A', status: Task.statusNew),
- generateTask(3, name: 'Linux_android A', status: Task.statusFailed),
- ];
- db.addOnQuery<Task>((Iterable<Task> results) => allGray);
- await tester.get(handler);
- expect(pubsub.messages.length, 0);
- });
-
- test('backfills older task', () async {
- final List<Task> oldestGray = <Task>[
- generateTask(1, name: 'Linux_android A', status: Task.statusSucceeded),
- generateTask(2, name: 'Linux_android A', status: Task.statusSucceeded),
- generateTask(3, name: 'Linux_android A', status: Task.statusNew),
- ];
- db.addOnQuery<Task>((Iterable<Task> results) => oldestGray);
- await tester.get(handler);
- expect(pubsub.messages.length, 1);
- });
-
- test('updates task as in-progress after backfilling', () async {
- final List<Task> oldestGray = <Task>[
- generateTask(1, name: 'Linux_android A', status: Task.statusSucceeded),
- generateTask(2, name: 'Linux_android A', status: Task.statusSucceeded),
- generateTask(3, name: 'Linux_android A', status: Task.statusNew),
- ];
- db.addOnQuery<Task>((Iterable<Task> results) => oldestGray);
- final Task task = oldestGray[2];
- expect(db.values.length, 0);
- expect(task.status, Task.statusNew);
- await tester.get(handler);
- expect(db.values.length, 1);
- expect(task.status, Task.statusInProgress);
- });
-
- test('skip scheduling builds if datastore commit fails', () async {
- db.commitException = true;
- final List<Task> oldestGray = <Task>[
- generateTask(1, name: 'Linux_android A', status: Task.statusSucceeded),
- generateTask(2, name: 'Linux_android A', status: Task.statusSucceeded),
- generateTask(3, name: 'Linux_android A', status: Task.statusNew),
- ];
- db.addOnQuery<Task>((Iterable<Task> results) => oldestGray);
- expect(db.values.length, 0);
- await tester.get(handler);
- expect(db.values.length, 0);
- expect(pubsub.messages.length, 0);
- });
-
- test('backfills only column A when B does need backfill', () async {
- final List<Task> scheduleA = <Task>[
- // Linux_android A
- generateTask(1, name: 'Linux_android A', status: Task.statusSucceeded),
- generateTask(2, name: 'Linux_android A', status: Task.statusSucceeded),
- generateTask(3, name: 'Linux_android A', status: Task.statusNew),
- // Linux_android B
- generateTask(1, name: 'Linux_android B', status: Task.statusSucceeded),
- generateTask(2, name: 'Linux_android B', status: Task.statusSucceeded),
- generateTask(3, name: 'Linux_android B', status: Task.statusSucceeded),
- ];
- db.addOnQuery<Task>((Iterable<Task> results) => scheduleA);
- await tester.get(handler);
- expect(pubsub.messages.length, 1);
- });
-
- test('backfills both column A and B', () async {
- final List<Task> scheduleA = <Task>[
- // Linux_android A
- generateTask(1, name: 'Linux_android A', status: Task.statusSucceeded),
- generateTask(2, name: 'Linux_android A', status: Task.statusSucceeded),
- generateTask(3, name: 'Linux_android A', status: Task.statusNew),
- // Linux_android B
- generateTask(1, name: 'Linux_android B', status: Task.statusSucceeded),
- generateTask(2, name: 'Linux_android B', status: Task.statusSucceeded),
- generateTask(3, name: 'Linux_android B', status: Task.statusNew),
- ];
- db.addOnQuery<Task>((Iterable<Task> results) => scheduleA);
- await tester.get(handler);
- expect(pubsub.messages.length, 2);
- });
-
- test('backfills limited targets when number of available targets exceeds backfillerTargetLimit ', () async {
- final List<Task> scheduleA = <Task>[
- // Linux_android A
- generateTask(1, name: 'Linux_android A', status: Task.statusNew),
- generateTask(2, name: 'Linux_android A', status: Task.statusNew),
- // Linux_android B
- generateTask(1, name: 'Linux_android B', status: Task.statusNew),
- generateTask(2, name: 'Linux_android B', status: Task.statusNew),
- // Linux_android C
- generateTask(1, name: 'Linux_android C', status: Task.statusNew),
- generateTask(2, name: 'Linux_android C', status: Task.statusNew),
- ];
- db.addOnQuery<Task>((Iterable<Task> results) => scheduleA);
- await tester.get(handler);
- expect(pubsub.messages.length, 2);
- });
-
- group('getFilteredBackfill', () {
- test('backfills high priorty targets first', () async {
- final List<Tuple<Target, FullTask, int>> backfill = <Tuple<Target, FullTask, int>>[
- Tuple(generateTarget(1), FullTask(generateTask(1), generateCommit(1)), LuciBuildService.kRerunPriority),
- Tuple(generateTarget(2), FullTask(generateTask(2), generateCommit(2)), LuciBuildService.kBackfillPriority),
- Tuple(generateTarget(3), FullTask(generateTask(3), generateCommit(3)), LuciBuildService.kRerunPriority),
- ];
- final List<Tuple<Target, FullTask, int>> filteredBackfill = handler.getFilteredBackfill(backfill);
- expect(filteredBackfill.length, 2);
- expect(filteredBackfill[0].third, LuciBuildService.kRerunPriority);
- expect(filteredBackfill[1].third, LuciBuildService.kRerunPriority);
- });
- });
- });
-}
diff --git a/app_dart/test/request_handlers/scheduler/request_subscription_test.dart b/app_dart/test/request_handlers/scheduler/request_subscription_test.dart
deleted file mode 100644
index 0428343..0000000
--- a/app_dart/test/request_handlers/scheduler/request_subscription_test.dart
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright 2019 The Flutter 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:convert';
-
-import 'package:cocoon_service/cocoon_service.dart';
-import 'package:cocoon_service/src/model/luci/buildbucket.dart';
-import 'package:cocoon_service/src/model/luci/push_message.dart' as push_message;
-import 'package:cocoon_service/src/request_handling/exceptions.dart';
-import 'package:mockito/mockito.dart';
-import 'package:retry/retry.dart';
-import 'package:test/test.dart';
-
-import '../../src/datastore/fake_config.dart';
-import '../../src/request_handling/fake_authentication.dart';
-import '../../src/request_handling/fake_http.dart';
-import '../../src/request_handling/subscription_tester.dart';
-import '../../src/utilities/mocks.dart';
-
-void main() {
- late SchedulerRequestSubscription handler;
- late SubscriptionTester tester;
-
- late MockBuildBucketClient buildBucketClient;
-
- setUp(() async {
- buildBucketClient = MockBuildBucketClient();
- when(buildBucketClient.batch(any)).thenAnswer((_) async => const BatchResponse());
- handler = SchedulerRequestSubscription(
- cache: CacheService(inMemory: true),
- config: FakeConfig(),
- authProvider: FakeAuthenticationProvider(),
- buildBucketClient: buildBucketClient,
- retryOptions: const RetryOptions(
- maxAttempts: 3,
- maxDelay: Duration.zero,
- ),
- );
- tester = SubscriptionTester(
- request: FakeHttpRequest(),
- );
- });
-
- test('throws exception when BatchRequest cannot be decoded', () async {
- tester.message = const push_message.PushMessage();
- expect(() => tester.post(handler), throwsA(isA<BadRequestException>()));
- });
-
- test('schedules request to buildbucket', () async {
- const BatchRequest request = BatchRequest();
- tester.message = push_message.PushMessage(data: base64Encode(utf8.encode(jsonEncode(request))));
- final Body body = await tester.post(handler);
- expect(body, Body.empty);
- });
-
- test('retries schedule build if no response comes back', () async {
- int attempt = 0;
- when(buildBucketClient.batch(any)).thenAnswer((_) async {
- attempt += 1;
- if (attempt == 2) {
- return const BatchResponse(
- responses: <Response>[
- Response(
- scheduleBuild: Build(
- id: '12345',
- builderId: BuilderId(builder: 'Linux A'),
- ),
- ),
- ],
- );
- }
-
- return const BatchResponse();
- });
- const BatchRequest request = BatchRequest(
- requests: <Request>[
- Request(
- scheduleBuild: ScheduleBuildRequest(
- builderId: BuilderId(
- builder: 'Linux A',
- ),
- ),
- ),
- ],
- );
- tester.message = push_message.PushMessage(data: base64Encode(utf8.encode(jsonEncode(request))));
- final Body body = await tester.post(handler);
- expect(body, Body.empty);
- expect(verify(buildBucketClient.batch(any)).callCount, 2);
- });
-
- test('acking message and loging error when no response comes back after retry limit', () async {
- when(buildBucketClient.batch(any)).thenAnswer((_) async {
- return const BatchResponse();
- });
- const BatchRequest request = BatchRequest(
- requests: <Request>[
- Request(
- scheduleBuild: ScheduleBuildRequest(
- builderId: BuilderId(
- builder: 'Linux A',
- ),
- ),
- ),
- ],
- );
- tester.message = push_message.PushMessage(data: base64Encode(utf8.encode(jsonEncode(request))));
- final Body body = await tester.post(handler);
- expect(body, isNotNull);
- expect(verify(buildBucketClient.batch(any)).callCount, 3);
- });
-}
diff --git a/app_dart/test/request_handlers/scheduler/vacuum_stale_tasks_test.dart b/app_dart/test/request_handlers/scheduler/vacuum_stale_tasks_test.dart
deleted file mode 100644
index 3135b79..0000000
--- a/app_dart/test/request_handlers/scheduler/vacuum_stale_tasks_test.dart
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:cocoon_service/cocoon_service.dart';
-import 'package:cocoon_service/src/model/appengine/commit.dart';
-import 'package:cocoon_service/src/model/appengine/task.dart';
-import 'package:cocoon_service/src/service/datastore.dart';
-import 'package:gcloud/db.dart';
-import 'package:test/test.dart';
-
-import '../../src/datastore/fake_config.dart';
-import '../../src/request_handling/request_handler_tester.dart';
-import '../../src/utilities/entity_generators.dart';
-
-void main() {
- group(VacuumStaleTasks, () {
- late FakeConfig config;
- late RequestHandlerTester tester;
- late VacuumStaleTasks handler;
-
- final Commit commit = generateCommit(1);
- final DateTime now = DateTime(2023, 2, 9, 13, 37);
-
- /// Helper function for returning test times relative to [now].
- DateTime relativeToNow(int minutes) {
- final Duration duration = Duration(minutes: minutes);
-
- return now.subtract(duration);
- }
-
- setUp(() {
- config = FakeConfig();
- config.db.values[commit.key] = commit;
-
- tester = RequestHandlerTester();
- handler = VacuumStaleTasks(
- config: config,
- datastoreProvider: (DatastoreDB db) => DatastoreService(config.db, 5),
- );
- });
-
- test('skips when no tasks are stale', () async {
- final List<Task> expectedTasks = <Task>[
- generateTask(
- 1,
- status: Task.statusInProgress,
- created: relativeToNow(1),
- parent: commit,
- ),
- generateTask(
- 2,
- status: Task.statusSucceeded,
- created: relativeToNow(VacuumStaleTasks.kTimeoutLimit.inMinutes + 5),
- parent: commit,
- ),
- generateTask(
- 3,
- status: Task.statusInProgress,
- created: relativeToNow(VacuumStaleTasks.kTimeoutLimit.inMinutes),
- parent: commit,
- ),
- ];
- await config.db.commit(inserts: expectedTasks);
-
- await tester.get(handler);
-
- final List<Task> tasks = config.db.values.values.whereType<Task>().toList();
- expect(tasks, expectedTasks);
- });
-
- test('resets stale task', () async {
- final List<Task> originalTasks = <Task>[
- generateTask(
- 1,
- status: Task.statusInProgress,
- created: relativeToNow(1),
- parent: commit,
- ),
- generateTask(
- 2,
- status: Task.statusSucceeded,
- created: relativeToNow(VacuumStaleTasks.kTimeoutLimit.inMinutes + 5),
- parent: commit,
- ),
- // Task 3 should be vacuumed
- generateTask(
- 3,
- status: Task.statusInProgress,
- created: relativeToNow(VacuumStaleTasks.kTimeoutLimit.inMinutes + 1),
- parent: commit,
- ),
- ];
- final DatastoreService datastore = DatastoreService(config.db, 5);
- await datastore.insert(originalTasks);
-
- await tester.get(handler);
-
- final List<Task> tasks = config.db.values.values.whereType<Task>().toList();
- expect(tasks[0], originalTasks[0]);
- expect(tasks[1], originalTasks[1]);
- expect(tasks[2].status, Task.statusNew);
- expect(tasks[2].createTimestamp, 0);
- });
- });
-}
diff --git a/app_dart/test/request_handlers/update_existing_flaky_issues_test.dart b/app_dart/test/request_handlers/update_existing_flaky_issues_test.dart
deleted file mode 100644
index 698ec38..0000000
--- a/app_dart/test/request_handlers/update_existing_flaky_issues_test.dart
+++ /dev/null
@@ -1,474 +0,0 @@
-// Copyright 2019 The Flutter 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:convert';
-
-import 'package:cocoon_service/cocoon_service.dart';
-import 'package:cocoon_service/src/request_handlers/flaky_handler_utils.dart';
-import 'package:cocoon_service/src/service/bigquery.dart';
-import 'package:cocoon_service/src/service/github_service.dart';
-import 'package:github/github.dart';
-import 'package:http/http.dart';
-import 'package:mockito/mockito.dart';
-import 'package:test/test.dart';
-
-import '../src/datastore/fake_config.dart';
-import '../src/request_handling/api_request_handler_tester.dart';
-import '../src/request_handling/fake_authentication.dart';
-import '../src/request_handling/fake_http.dart';
-import '../src/utilities/mocks.dart';
-
-import 'update_existing_flaky_issues_test_data.dart';
-
-const String kThreshold = '0.02';
-
-void main() {
- group('Update flaky', () {
- late UpdateExistingFlakyIssue handler;
- late ApiRequestHandlerTester tester;
- FakeHttpRequest request;
- late FakeConfig config;
- FakeClientContext clientContext;
- FakeAuthenticationProvider auth;
- late MockBigqueryService mockBigqueryService;
- late MockGitHub mockGitHubClient;
- late MockIssuesService mockIssuesService;
- MockRepositoriesService mockRepositoriesService;
-
- setUp(() {
- request = FakeHttpRequest(
- queryParametersValue: <String, dynamic>{
- FileFlakyIssueAndPR.kThresholdKey: kThreshold,
- },
- );
-
- clientContext = FakeClientContext();
- auth = FakeAuthenticationProvider(clientContext: clientContext);
- mockBigqueryService = MockBigqueryService();
- mockGitHubClient = MockGitHub();
- mockIssuesService = MockIssuesService();
- mockRepositoriesService = MockRepositoriesService();
-
- // when gets existing flaky issues.
- when(mockIssuesService.listByRepo(captureAny, state: captureAnyNamed('state'), labels: captureAnyNamed('labels')))
- .thenAnswer((Invocation invocation) {
- return const Stream<Issue>.empty();
- });
-
- // when gets the content of TESTOWNERS
- when(
- mockRepositoriesService.getContents(
- captureAny,
- kTestOwnerPath,
- ),
- ).thenAnswer((Invocation invocation) {
- return Future<RepositoryContents>.value(
- RepositoryContents(file: GitHubFile(content: gitHubEncode(testOwnersContent))),
- );
- });
-
- when(mockGitHubClient.repositories).thenReturn(mockRepositoriesService);
- when(mockGitHubClient.issues).thenReturn(mockIssuesService);
- when(mockIssuesService.createComment(any, any, any)).thenAnswer((_) async => IssueComment());
- when(mockIssuesService.edit(any, any, any)).thenAnswer((_) async => Issue());
- config = FakeConfig(
- githubService: GithubService(mockGitHubClient),
- bigqueryService: mockBigqueryService,
- githubOAuthTokenValue: 'token',
- );
- tester = ApiRequestHandlerTester(request: request);
-
- handler = UpdateExistingFlakyIssue(
- config: config,
- authenticationProvider: auth,
- ciYaml: testCiYaml,
- );
- });
-
- test('Can add existing issue comment', () async {
- const int existingIssueNumber = 1234;
- final List<IssueLabel> existingLabels = <IssueLabel>[
- IssueLabel(name: 'some random label'),
- IssueLabel(name: 'P2'),
- ];
- // When queries flaky data from BigQuery.
- when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId)).thenAnswer((Invocation invocation) {
- return Future<List<BuilderStatistic>>.value(semanticsIntegrationTestResponse);
- });
- when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging'))
- .thenAnswer((Invocation invocation) {
- return Future<List<BuilderStatistic>>.value(stagingCiyamlTestResponse);
- });
- // when gets existing flaky issues.
- when(mockIssuesService.listByRepo(captureAny, state: captureAnyNamed('state'), labels: captureAnyNamed('labels')))
- .thenAnswer((Invocation invocation) {
- return Stream<Issue>.fromIterable(<Issue>[
- Issue(
- assignee: User(login: 'some dude'),
- number: existingIssueNumber,
- state: 'open',
- labels: existingLabels,
- title: expectedSemanticsIntegrationTestResponseTitle,
- body: expectedSemanticsIntegrationTestResponseBody,
- createdAt:
- DateTime.now().subtract(const Duration(days: UpdateExistingFlakyIssue.kFreshPeriodForOpenFlake + 1)),
- ),
- ]);
- });
- // when firing github request.
- // This is for replacing labels.
- when(
- mockGitHubClient.request(
- captureAny,
- captureAny,
- body: captureAnyNamed('body'),
- ),
- ).thenAnswer((Invocation invocation) {
- return Future<Response>.value(Response('[]', 200));
- });
- final Map<String, dynamic> result = await utf8.decoder
- .bind((await tester.get<Body>(handler)).serialize() as Stream<List<int>>)
- .transform(json.decoder)
- .single as Map<String, dynamic>;
-
- // Verify comment is created correctly.
- List<dynamic> captured = verify(mockIssuesService.createComment(captureAny, captureAny, captureAny)).captured;
- expect(captured.length, 3);
- expect(captured[0].toString(), Config.flutterSlug.toString());
- expect(captured[1], existingIssueNumber);
- expect(captured[2], expectedSemanticsIntegrationTestIssueComment);
-
- // Verify labels are applied correctly.
- captured = verify(
- mockGitHubClient.request(
- captureAny,
- captureAny,
- body: captureAnyNamed('body'),
- ),
- ).captured;
- expect(captured.length, 3);
- expect(captured[0].toString(), 'PUT');
- expect(captured[1], '/repos/${Config.flutterSlug.fullName}/issues/$existingIssueNumber/labels');
- expect(captured[2], GitHubJson.encode(<String>['some random label', 'P0']));
-
- expect(result['Status'], 'success');
- });
-
- test('Add only one comment on existing issue when a builder has been marked as unflaky', () async {
- const int existingIssueNumber = 1234;
- final List<IssueLabel> existingLabels = <IssueLabel>[
- IssueLabel(name: 'some random label'),
- IssueLabel(name: 'P2'),
- ];
- // When queries flaky data from BigQuery.
- when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId)).thenAnswer((Invocation invocation) {
- return Future<List<BuilderStatistic>>.value(semanticsIntegrationTestResponse);
- });
- when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging'))
- .thenAnswer((Invocation invocation) {
- return Future<List<BuilderStatistic>>.value(stagingSameBuilderSemanticsIntegrationTestResponse);
- });
- // when gets existing flaky issues.
- when(mockIssuesService.listByRepo(captureAny, state: captureAnyNamed('state'), labels: captureAnyNamed('labels')))
- .thenAnswer((Invocation invocation) {
- return Stream<Issue>.fromIterable(<Issue>[
- Issue(
- assignee: User(login: 'some dude'),
- number: existingIssueNumber,
- state: 'open',
- labels: existingLabels,
- title: expectedSemanticsIntegrationTestResponseTitle,
- body: expectedSemanticsIntegrationTestResponseBody,
- createdAt:
- DateTime.now().subtract(const Duration(days: UpdateExistingFlakyIssue.kFreshPeriodForOpenFlake + 1)),
- ),
- ]);
- });
- // when firing github request.
- // This is for replacing labels.
- when(
- mockGitHubClient.request(
- captureAny,
- captureAny,
- body: captureAnyNamed('body'),
- ),
- ).thenAnswer((Invocation invocation) {
- return Future<Response>.value(Response('[]', 200));
- });
- final Map<String, dynamic> result = await utf8.decoder
- .bind((await tester.get<Body>(handler)).serialize() as Stream<List<int>>)
- .transform(json.decoder)
- .single as Map<String, dynamic>;
-
- // Verify comment is created correctly.
- List<dynamic> captured = verify(mockIssuesService.createComment(captureAny, captureAny, captureAny)).captured;
- expect(captured.length, 3);
- expect(captured[0].toString(), Config.flutterSlug.toString());
- expect(captured[1], existingIssueNumber);
- expect(captured[2], expectedSemanticsIntegrationTestIssueComment);
-
- // Verify labels are applied correctly.
- captured = verify(
- mockGitHubClient.request(
- captureAny,
- captureAny,
- body: captureAnyNamed('body'),
- ),
- ).captured;
- expect(captured.length, 3);
- expect(captured[0].toString(), 'PUT');
- expect(captured[1], '/repos/${Config.flutterSlug.fullName}/issues/$existingIssueNumber/labels');
- expect(captured[2], GitHubJson.encode(<String>['some random label', 'P0']));
-
- expect(result['Status'], 'success');
- });
-
- test('Can add bot staging and prod stats for a bringup: true builder', () async {
- const int existingIssueNumber = 1234;
- final List<IssueLabel> existingLabels = <IssueLabel>[
- IssueLabel(name: 'some random label'),
- IssueLabel(name: 'P2'),
- ];
- // When queries flaky data from BigQuery.
- when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId)).thenAnswer((Invocation invocation) {
- return Future<List<BuilderStatistic>>.value(ciyamlTestResponse);
- });
- when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging'))
- .thenAnswer((Invocation invocation) {
- return Future<List<BuilderStatistic>>.value(stagingCiyamlTestResponse);
- });
- // when gets existing flaky issues.
- when(mockIssuesService.listByRepo(captureAny, state: captureAnyNamed('state'), labels: captureAnyNamed('labels')))
- .thenAnswer((Invocation invocation) {
- return Stream<Issue>.fromIterable(<Issue>[
- Issue(
- assignee: User(login: 'some dude'),
- number: existingIssueNumber,
- state: 'open',
- labels: existingLabels,
- title: expectedStagingSemanticsIntegrationTestResponseTitle,
- body: expectedStagingSemanticsIntegrationTestResponseBody,
- createdAt:
- DateTime.now().subtract(const Duration(days: UpdateExistingFlakyIssue.kFreshPeriodForOpenFlake + 1)),
- ),
- ]);
- });
- // when firing github request.
- // This is for replacing labels.
- when(
- mockGitHubClient.request(
- captureAny,
- captureAny,
- body: captureAnyNamed('body'),
- ),
- ).thenAnswer((Invocation invocation) {
- return Future<Response>.value(Response('[]', 200));
- });
- final Map<String, dynamic> result = await utf8.decoder
- .bind((await tester.get<Body>(handler)).serialize() as Stream<List<int>>)
- .transform(json.decoder)
- .single as Map<String, dynamic>;
-
- // Verify comment is created correctly.
- List<dynamic> captured = verify(mockIssuesService.createComment(captureAny, captureAny, captureAny)).captured;
- expect(captured.length, 6);
- expect(captured[0].toString(), Config.flutterSlug.toString());
- expect(captured[1], existingIssueNumber);
- expect(captured[2], expectedCiyamlTestIssueComment);
- expect(captured[3].toString(), Config.flutterSlug.toString());
- expect(captured[4], existingIssueNumber);
- expect(captured[5], expectedStagingCiyamlTestIssueComment);
-
- // Verify labels are applied correctly.
- captured = verify(
- mockGitHubClient.request(
- captureAny,
- captureAny,
- body: captureAnyNamed('body'),
- ),
- ).captured;
- expect(captured.length, 6);
- expect(captured[0].toString(), 'PUT');
- expect(captured[1], '/repos/${Config.flutterSlug.fullName}/issues/$existingIssueNumber/labels');
- expect(captured[2], GitHubJson.encode(<String>['some random label', 'P0']));
-
- expect(result['Status'], 'success');
- });
-
- test('Can assign test owner', () async {
- const int existingIssueNumber = 1234;
- final List<IssueLabel> existingLabels = <IssueLabel>[
- IssueLabel(name: 'some random label'),
- IssueLabel(name: 'P2'),
- ];
- // When queries flaky data from BigQuery.
- when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId)).thenAnswer((Invocation invocation) {
- return Future<List<BuilderStatistic>>.value(semanticsIntegrationTestResponse);
- });
- when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging'))
- .thenAnswer((Invocation invocation) {
- return Future<List<BuilderStatistic>>.value(stagingCiyamlTestResponse);
- });
- // when gets existing flaky issues.
- when(mockIssuesService.listByRepo(captureAny, state: captureAnyNamed('state'), labels: captureAnyNamed('labels')))
- .thenAnswer((Invocation invocation) {
- return Stream<Issue>.fromIterable(<Issue>[
- Issue(
- number: existingIssueNumber,
- state: 'open',
- labels: existingLabels,
- title: expectedSemanticsIntegrationTestResponseTitle,
- body: expectedSemanticsIntegrationTestResponseBody,
- createdAt:
- DateTime.now().subtract(const Duration(days: UpdateExistingFlakyIssue.kFreshPeriodForOpenFlake + 1)),
- ),
- ]);
- });
- // when firing github request.
- // This is for replacing labels.
- when(
- mockGitHubClient.request(
- captureAny,
- captureAny,
- body: captureAnyNamed('body'),
- ),
- ).thenAnswer((Invocation invocation) {
- return Future<Response>.value(Response('[]', 200));
- });
-
- final Map<String, dynamic> result = await utf8.decoder
- .bind((await tester.get<Body>(handler)).serialize() as Stream<List<int>>)
- .transform(json.decoder)
- .single as Map<String, dynamic>;
-
- // Verify comment is created correctly.
- final List<dynamic> captured = verify(mockIssuesService.edit(captureAny, captureAny, captureAny)).captured;
- expect(captured.length, 3);
- expect(captured[0].toString(), Config.flutterSlug.toString());
- expect(captured[1], existingIssueNumber);
- final IssueRequest request = captured[2] as IssueRequest;
- expect(request.assignee, 'HansMuller');
-
- expect(result['Status'], 'success');
- });
-
- test('Can add existing issue comment case 0.0', () async {
- const int existingIssueNumber = 1234;
- final List<IssueLabel> existingLabels = <IssueLabel>[
- IssueLabel(name: 'some random label'),
- IssueLabel(name: 'P2'),
- ];
- // When queries flaky data from BigQuery.
- when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId)).thenAnswer((Invocation invocation) {
- return Future<List<BuilderStatistic>>.value(semanticsIntegrationTestResponseZeroFlake);
- });
- when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging'))
- .thenAnswer((Invocation invocation) {
- return Future<List<BuilderStatistic>>.value(stagingCiyamlTestResponse);
- });
- // when gets existing flaky issues.
- when(mockIssuesService.listByRepo(captureAny, state: captureAnyNamed('state'), labels: captureAnyNamed('labels')))
- .thenAnswer((Invocation invocation) {
- return Stream<Issue>.fromIterable(<Issue>[
- Issue(
- number: existingIssueNumber,
- state: 'open',
- labels: existingLabels,
- title: expectedSemanticsIntegrationTestResponseTitle,
- body: expectedSemanticsIntegrationTestResponseBody,
- createdAt:
- DateTime.now().subtract(const Duration(days: UpdateExistingFlakyIssue.kFreshPeriodForOpenFlake + 1)),
- ),
- ]);
- });
- // when firing github request.
- // This is for replacing labels.
- when(
- mockGitHubClient.request(
- captureAny,
- captureAny,
- body: captureAnyNamed('body'),
- ),
- ).thenAnswer((Invocation invocation) {
- return Future<Response>.value(Response('[]', 200));
- });
-
- final Map<String, dynamic> result = await utf8.decoder
- .bind((await tester.get<Body>(handler)).serialize() as Stream<List<int>>)
- .transform(json.decoder)
- .single as Map<String, dynamic>;
-
- // Verify issue is created correctly.
- List<dynamic> captured = verify(mockIssuesService.createComment(captureAny, captureAny, captureAny)).captured;
- expect(captured.length, 3);
- expect(captured[0].toString(), Config.flutterSlug.toString());
- expect(captured[1], existingIssueNumber);
- expect(captured[2], expectedSemanticsIntegrationTestZeroFlakeIssueComment);
-
- // Verify labels are the same.
- captured = verify(
- mockGitHubClient.request(
- captureAny,
- captureAny,
- body: captureAnyNamed('body'),
- ),
- ).captured;
- expect(captured.length, 3);
- expect(captured[0].toString(), 'PUT');
- expect(captured[1], '/repos/${Config.flutterSlug.fullName}/issues/$existingIssueNumber/labels');
- expect(captured[2], GitHubJson.encode(<String>['some random label', 'P2']));
-
- expect(result['Status'], 'success');
- });
-
- test('Does not add comment if the issue is still fresh', () async {
- const int existingIssueNumber = 1234;
- final List<IssueLabel> existingLabels = <IssueLabel>[
- IssueLabel(name: 'some random label'),
- IssueLabel(name: 'P2'),
- ];
- // When queries flaky data from BigQuery.
- when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId)).thenAnswer((Invocation invocation) {
- return Future<List<BuilderStatistic>>.value(semanticsIntegrationTestResponseZeroFlake);
- });
- when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging'))
- .thenAnswer((Invocation invocation) {
- return Future<List<BuilderStatistic>>.value(stagingCiyamlTestResponse);
- });
- // when gets existing flaky issues.
- when(mockIssuesService.listByRepo(captureAny, state: captureAnyNamed('state'), labels: captureAnyNamed('labels')))
- .thenAnswer((Invocation invocation) {
- return Stream<Issue>.fromIterable(<Issue>[
- Issue(
- number: existingIssueNumber,
- state: 'open',
- labels: existingLabels,
- title: expectedSemanticsIntegrationTestResponseTitle,
- body: expectedSemanticsIntegrationTestResponseBody,
- createdAt:
- DateTime.now().subtract(const Duration(days: UpdateExistingFlakyIssue.kFreshPeriodForOpenFlake - 1)),
- ),
- ]);
- });
-
- final Map<String, dynamic> result = await utf8.decoder
- .bind((await tester.get<Body>(handler)).serialize() as Stream<List<int>>)
- .transform(json.decoder)
- .single as Map<String, dynamic>;
-
- verifyNever(mockIssuesService.createComment(captureAny, captureAny, captureAny));
-
- // Verify labels are the same.
- verifyNever(
- mockGitHubClient.request(
- captureAny,
- captureAny,
- body: captureAnyNamed('body'),
- ),
- );
-
- expect(result['Status'], 'success');
- });
- });
-}
diff --git a/app_dart/test/request_handlers/update_existing_flaky_issues_test_data.dart b/app_dart/test/request_handlers/update_existing_flaky_issues_test_data.dart
deleted file mode 100644
index 5699d2b..0000000
--- a/app_dart/test/request_handlers/update_existing_flaky_issues_test_data.dart
+++ /dev/null
@@ -1,336 +0,0 @@
-// Copyright 2019 The Flutter 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:convert';
-
-import 'package:cocoon_service/src/model/ci_yaml/ci_yaml.dart';
-import 'package:cocoon_service/src/service/bigquery.dart';
-import 'package:cocoon_service/src/service/config.dart';
-
-import 'package:cocoon_service/src/model/proto/protos.dart' as pb;
-
-const String expectedSemanticsIntegrationTestIssueComment = '''
-[prod pool] flaky ratio for the past (up to) 100 commits between 2023-06-20 and 2023-06-29 is 50.00%. Flaky number: 3; total number: 10.
-One recent flaky example for a same commit: https://ci.chromium.org/ui/p/flutter/builders/prod/Mac_android%20android_semantics_integration_test/103
-Commit: https://github.com/flutter/flutter/commit/abc
-Flaky builds:
-https://ci.chromium.org/ui/p/flutter/builders/prod/Mac_android%20android_semantics_integration_test/103
-https://ci.chromium.org/ui/p/flutter/builders/prod/Mac_android%20android_semantics_integration_test/102
-https://ci.chromium.org/ui/p/flutter/builders/prod/Mac_android%20android_semantics_integration_test/101
-
-Recent test runs:
-https://flutter-dashboard.appspot.com/#/build?taskFilter=Mac_android%20android_semantics_integration_test
-''';
-
-const String expectedCiyamlTestIssueComment = '''
-[prod pool] flaky ratio for the past (up to) 100 commits between 2023-06-20 and 2023-06-29 is 50.00%. Flaky number: 3; total number: 10.
-One recent flaky example for a same commit: https://ci.chromium.org/ui/p/flutter/builders/prod/Linux%20ci_yaml%20flutter%20roller/103
-Commit: https://github.com/flutter/flutter/commit/abc
-Flaky builds:
-https://ci.chromium.org/ui/p/flutter/builders/prod/Linux%20ci_yaml%20flutter%20roller/103
-https://ci.chromium.org/ui/p/flutter/builders/prod/Linux%20ci_yaml%20flutter%20roller/102
-https://ci.chromium.org/ui/p/flutter/builders/prod/Linux%20ci_yaml%20flutter%20roller/101
-
-Recent test runs:
-https://flutter-dashboard.appspot.com/#/build?taskFilter=Linux%20ci_yaml%20flutter%20roller
-''';
-
-const String expectedStagingCiyamlTestIssueComment = '''
-[staging pool] flaky ratio for the past (up to) 100 commits between 2023-06-20 and 2023-06-29 is 50.00%. Flaky number: 3; total number: 10.
-One recent flaky example for a same commit: https://ci.chromium.org/ui/p/flutter/builders/staging/Linux%20ci_yaml%20flutter%20roller/103
-Commit: https://github.com/flutter/flutter/commit/abc
-Flaky builds:
-https://ci.chromium.org/ui/p/flutter/builders/staging/Linux%20ci_yaml%20flutter%20roller/103
-https://ci.chromium.org/ui/p/flutter/builders/staging/Linux%20ci_yaml%20flutter%20roller/102
-https://ci.chromium.org/ui/p/flutter/builders/staging/Linux%20ci_yaml%20flutter%20roller/101
-
-Recent test runs:
-https://flutter-dashboard.appspot.com/#/build?taskFilter=Linux%20ci_yaml%20flutter%20roller
-''';
-
-const String expectedSemanticsIntegrationTestZeroFlakeIssueComment = '''
-[prod pool] flaky ratio for the past (up to) 100 commits between 2023-06-20 and 2023-06-29 is 0.00%. Flaky number: 0; total number: 10.
-''';
-
-const String expectedSemanticsIntegrationTestNotEnoughDataComment = '''
-Current flaky ratio is not available (< 10 commits).
-''';
-
-final List<BuilderStatistic> semanticsIntegrationTestResponseZeroFlake = <BuilderStatistic>[
- BuilderStatistic(
- name: 'Mac_android android_semantics_integration_test',
- flakyRate: 0.0,
- flakyBuilds: <String>[],
- succeededBuilds: <String>['203', '202', '201', '200', '199', '198', '197', '196', '195', '194'],
- recentCommit: '',
- flakyBuildOfRecentCommit: '',
- flakyNumber: 0,
- totalNumber: 10,
- fromDate: '2023-06-20',
- toDate: '2023-06-29',
- ),
-];
-
-final List<BuilderStatistic> semanticsIntegrationTestResponse = <BuilderStatistic>[
- BuilderStatistic(
- name: 'Mac_android android_semantics_integration_test',
- flakyRate: 0.5,
- flakyBuilds: <String>['103', '102', '101'],
- succeededBuilds: <String>['203', '202', '201', '200', '199', '198', '197'],
- recentCommit: 'abc',
- flakyBuildOfRecentCommit: '103',
- flakyNumber: 3,
- totalNumber: 10,
- fromDate: '2023-06-20',
- toDate: '2023-06-29',
- ),
-];
-
-final List<BuilderStatistic> stagingSameBuilderSemanticsIntegrationTestResponse = <BuilderStatistic>[
- BuilderStatistic(
- name: 'Mac_android android_semantics_integration_test',
- flakyRate: 0.5,
- flakyBuilds: <String>['103', '102', '101'],
- succeededBuilds: <String>['203', '202', '201', '200', '199', '198', '197'],
- recentCommit: 'abc',
- flakyBuildOfRecentCommit: '103',
- flakyNumber: 3,
- totalNumber: 10,
- fromDate: '2023-06-20',
- toDate: '2023-06-29',
- ),
-];
-
-final List<BuilderStatistic> semanticsIntegrationTestResponseNotEnoughData = <BuilderStatistic>[
- BuilderStatistic(
- name: 'Mac_android android_semantics_integration_test',
- flakyRate: 0.5,
- flakyBuilds: <String>['103', '102', '101'],
- succeededBuilds: <String>['203', '202', '201', '200'],
- recentCommit: 'abc',
- flakyBuildOfRecentCommit: '103',
- flakyNumber: 3,
- totalNumber: 7,
- fromDate: '2023-06-20',
- toDate: '2023-06-29',
- ),
- // This builder is flakey, but it should be
- // ignored because it has ignore_flakiness set.
- BuilderStatistic(
- name: 'Mac_android ignore_myflakiness',
- flakyRate: 0.5,
- flakyBuilds: <String>['103', '102', '101'],
- succeededBuilds: <String>['203', '202', '201', '200'],
- recentCommit: 'abc',
- flakyBuildOfRecentCommit: '103',
- flakyNumber: 3,
- totalNumber: 7,
- fromDate: '2023-06-20',
- toDate: '2023-06-29',
- ),
-];
-
-final List<BuilderStatistic> shardSemanticsIntegrationTestResponse = <BuilderStatistic>[
- BuilderStatistic(
- name: 'Mac build_tests_1_4',
- flakyRate: 0.5,
- flakyBuilds: <String>['103', '102', '101'],
- succeededBuilds: <String>['203', '202', '201', '200', '199', '198', '197'],
- recentCommit: 'abc',
- flakyBuildOfRecentCommit: '103',
- flakyNumber: 3,
- totalNumber: 10,
- fromDate: '2023-06-20',
- toDate: '2023-06-29',
- ),
-];
-
-final List<BuilderStatistic> ciyamlTestResponse = <BuilderStatistic>[
- BuilderStatistic(
- name: 'Linux ci_yaml flutter roller',
- flakyRate: 0.5,
- flakyBuilds: <String>['103', '102', '101'],
- succeededBuilds: <String>['203', '202', '201', '200', '199', '198', '197'],
- recentCommit: 'abc',
- flakyBuildOfRecentCommit: '103',
- flakyNumber: 3,
- totalNumber: 10,
- fromDate: '2023-06-20',
- toDate: '2023-06-29',
- ),
-];
-
-final List<BuilderStatistic> stagingCiyamlTestResponse = <BuilderStatistic>[
- BuilderStatistic(
- name: 'Linux ci_yaml flutter roller',
- flakyRate: 0.5,
- flakyBuilds: <String>['103', '102', '101'],
- succeededBuilds: <String>['203', '202', '201', '200', '199', '198', '197'],
- recentCommit: 'abc',
- flakyBuildOfRecentCommit: '103',
- flakyNumber: 3,
- totalNumber: 10,
- fromDate: '2023-06-20',
- toDate: '2023-06-29',
- ),
-];
-
-const String expectedSemanticsIntegrationTestResponseTitle =
- 'Mac_android android_semantics_integration_test is 50.00% flaky';
-const String expectedSemanticsIntegrationTestResponseBody = '''
-<!-- meta-tags: To be used by the automation script only, DO NOT MODIFY.
-{
- "name": "Mac_android android_semantics_integration_test"
-}
--->
-
-The post-submit test builder `Mac_android android_semantics_integration_test` had a flaky ratio 50.00% for the past 15 days, which is above our 2.00% threshold.
-
-One recent flaky example for a same commit: https://ci.chromium.org/ui/p/flutter/builders/prod/Mac_android%20android_semantics_integration_test/103
-Commit: https://github.com/flutter/flutter/commit/abc
-Flaky builds:
-https://ci.chromium.org/ui/p/flutter/builders/prod/Mac_android%20android_semantics_integration_test/103
-https://ci.chromium.org/ui/p/flutter/builders/prod/Mac_android%20android_semantics_integration_test/102
-https://ci.chromium.org/ui/p/flutter/builders/prod/Mac_android%20android_semantics_integration_test/101
-
-Recent test runs:
-https://flutter-dashboard.appspot.com/#/build?taskFilter=Mac_android%20android_semantics_integration_test
-
-Please follow https://github.com/flutter/flutter/wiki/Reducing-Test-Flakiness#fixing-flaky-tests to fix the flakiness and enable the test back after validating the fix (internal dashboard to validate: go/flutter_test_flakiness).
-''';
-
-const String expectedStagingSemanticsIntegrationTestResponseTitle = 'Linux ci_yaml flutter roller is 50.00% flaky';
-const String expectedStagingSemanticsIntegrationTestResponseBody = '''
-<!-- meta-tags: To be used by the automation script only, DO NOT MODIFY.
-{
- "name": "Linux ci_yaml flutter roller"
-}
--->
-
-The post-submit test builder `Linux ci_yaml flutter roller` had a flaky ratio 50.00% for the past 15 days, which is above our 2.00% threshold.
-
-One recent flaky example for a same commit: https://ci.chromium.org/ui/p/flutter/builders/prod/Linux%20ci_yaml%20flutter%20roller/103
-Commit: https://github.com/flutter/flutter/commit/abc
-Flaky builds:
-https://ci.chromium.org/ui/p/flutter/builders/prod/Linux%20ci_yaml%20flutter%20roller/103
-https://ci.chromium.org/ui/p/flutter/builders/prod/Linux%20ci_yaml%20flutter%20roller/102
-https://ci.chromium.org/ui/p/flutter/builders/prod/Linux%20ci_yaml%20flutter%20roller/101
-
-Recent test runs:
-https://flutter-dashboard.appspot.com/#/build?taskFilter=Linux%20ci_yaml_flutter%20roller
-
-Please follow https://github.com/flutter/flutter/wiki/Reducing-Test-Flakiness#fixing-flaky-tests to fix the flakiness and enable the test back after validating the fix (internal dashboard to validate: go/flutter_test_flakiness).
-''';
-
-final CiYaml testCiYaml = CiYaml(
- slug: Config.flutterSlug,
- branch: Config.defaultBranch(Config.flutterSlug),
- config: pb.SchedulerConfig(
- enabledBranches: <String>[
- Config.defaultBranch(Config.flutterSlug),
- ],
- targets: <pb.Target>[
- pb.Target(
- name: 'Mac_android android_semantics_integration_test',
- scheduler: pb.SchedulerSystem.luci,
- presubmit: false,
- properties: <String, String>{
- 'tags': jsonEncode(['devicelab']),
- 'task_name': 'android_semantics_integration_test',
- },
- ),
- pb.Target(
- name: 'Mac_android ignore_myflakiness',
- scheduler: pb.SchedulerSystem.luci,
- presubmit: false,
- properties: <String, String>{
- 'ignore_flakiness': 'true',
- 'tags': jsonEncode(['devicelab']),
- 'task_name': 'ignore_myflakiness',
- },
- ),
- pb.Target(
- name: 'Linux ci_yaml flutter roller',
- scheduler: pb.SchedulerSystem.luci,
- bringup: true,
- timeout: 30,
- runIf: ['.ci.yaml'],
- recipe: 'infra/ci_yaml',
- properties: <String, String>{
- 'tags': jsonEncode(['framework', 'hostonly', 'shard']),
- },
- ),
- pb.Target(
- name: 'Mac build_tests_1_4',
- scheduler: pb.SchedulerSystem.luci,
- recipe: 'flutter/flutter_drone',
- timeout: 60,
- properties: <String, String>{
- 'add_recipes_cq': 'true',
- 'shard': 'build_tests',
- 'subshard': '1_4',
- 'tags': jsonEncode(['framework', 'hostonly', 'shard']),
- 'dependencies': jsonEncode([
- {
- 'dependency': 'android_sdk',
- 'version': 'version:29.0',
- },
- {
- 'dependency': 'chrome_and_driver',
- 'version': 'version:84',
- },
- {
- 'dependency': 'xcode',
- 'version': '13a233',
- },
- {
- 'dependency': 'open_jdk',
- 'version': '11',
- },
- {
- 'dependency': 'gems',
- 'version': 'v3.3.14',
- },
- {
- 'dependency': 'goldctl',
- 'version': 'git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603',
- },
- ]),
- },
- ),
- ],
- ),
-);
-
-const String testOwnersContent = '''
-
-# Below is a list of Flutter team members' GitHub handles who are
-# test owners of this repository.
-#
-# These owners are mainly team leaders and their sub-teams. Please feel
-# free to claim ownership by adding your handle to corresponding tests.
-#
-# This file will be used as a reference when new flaky bugs are filed and
-# the TL will be assigned and the sub-team will be labeled by default
-# for further triage.
-
-## Linux Android DeviceLab tests
-/dev/devicelab/bin/tasks/android_semantics_integration_test.dart @HansMuller @flutter/framework
-
-## Host only framework tests
-# Linux analyze
-/dev/bots/analyze.dart @HansMuller @flutter/framework
-
-## Shards tests
-# framework_tests @HansMuller @flutter/framework
-
-
-/dev/devicelab/bin/tasks/android_semantics_integration_test.dart @HansMuller @flutter/framework
-''';
-
-String gitHubEncode(String source) {
- final List<int> utf8Characters = utf8.encode(source);
- final String base64encoded = base64Encode(utf8Characters);
- return base64encoded;
-}
diff --git a/app_dart/test/request_handlers/update_task_status_test.dart b/app_dart/test/request_handlers/update_task_status_test.dart
deleted file mode 100644
index 9325d1f..0000000
--- a/app_dart/test/request_handlers/update_task_status_test.dart
+++ /dev/null
@@ -1,174 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:cocoon_service/src/model/appengine/commit.dart';
-import 'package:cocoon_service/src/model/appengine/task.dart';
-import 'package:cocoon_service/src/request_handlers/update_task_status.dart';
-import 'package:cocoon_service/src/service/config.dart';
-import 'package:cocoon_service/src/service/datastore.dart';
-import 'package:gcloud/db.dart';
-import 'package:test/test.dart';
-
-import '../src/bigquery/fake_tabledata_resource.dart';
-import '../src/datastore/fake_config.dart';
-import '../src/datastore/fake_datastore.dart';
-import '../src/request_handling/api_request_handler_tester.dart';
-import '../src/request_handling/fake_authentication.dart';
-
-void main() {
- group('UpdateTaskStatus', () {
- late FakeConfig config;
- late ApiRequestHandlerTester tester;
- late UpdateTaskStatus handler;
- final FakeTabledataResource tabledataResourceApi = FakeTabledataResource();
- late Commit commit;
- const String commitSha = '78cbfbff4267643bb1913bc820f5ce8a3e591b40';
- const int taskId = 4506830800027648;
-
- setUp(() {
- final FakeDatastoreDB datastoreDB = FakeDatastoreDB();
- config = FakeConfig(
- dbValue: datastoreDB,
- tabledataResource: tabledataResourceApi,
- maxTaskRetriesValue: 2,
- );
- tester = ApiRequestHandlerTester();
- handler = UpdateTaskStatus(
- config: config,
- authenticationProvider: FakeAuthenticationProvider(),
- datastoreProvider: (DatastoreDB db) => DatastoreService(config.db, 5),
- );
- commit = Commit(
- key: config.db.emptyKey.append(Commit, id: 'flutter/flutter/master/$commitSha'),
- repository: Config.flutterSlug.fullName,
- sha: commitSha,
- timestamp: 123,
- );
- });
-
- test('TestFlaky is false when not injected', () async {
- final Task task = Task(
- key: commit.key.append(Task, id: taskId),
- name: 'integration_ui_ios',
- builderName: 'linux_integration_ui_ios',
- attempts: 1,
- status: Task.statusInProgress,
- isFlaky: false, // mark flaky so it doesn't get auto-retried
- commitKey: commit.key,
- );
- config.db.values[commit.key] = commit;
- config.db.values[task.key] = task;
- tester.requestData = <String, dynamic>{
- UpdateTaskStatus.gitBranchParam: 'master',
- UpdateTaskStatus.gitShaParam: commitSha,
- UpdateTaskStatus.newStatusParam: 'Failed',
- UpdateTaskStatus.builderNameParam: 'linux_integration_ui_ios',
- };
-
- await tester.post(handler);
-
- expect(task.isTestFlaky, false);
- });
-
- test('TestFlaky is true when injected', () async {
- final Task task = Task(
- key: commit.key.append(Task, id: taskId),
- name: 'integration_ui_ios',
- builderName: 'linux_integration_ui_ios',
- attempts: 1,
- status: Task.statusInProgress,
- isFlaky: false, // mark flaky so it doesn't get auto-retried
- commitKey: commit.key,
- );
- config.db.values[commit.key] = commit;
- config.db.values[task.key] = task;
- tester.requestData = <String, dynamic>{
- UpdateTaskStatus.gitBranchParam: 'master',
- UpdateTaskStatus.gitShaParam: commitSha,
- UpdateTaskStatus.newStatusParam: 'Failed',
- UpdateTaskStatus.builderNameParam: 'linux_integration_ui_ios',
- UpdateTaskStatus.testFlayParam: true,
- };
-
- await tester.post(handler);
-
- expect(task.isTestFlaky, true);
- });
-
- test('task name requests can update tasks', () async {
- final Task task = Task(
- key: commit.key.append(Task, id: taskId),
- name: 'integration_ui_ios',
- builderName: 'linux_integration_ui_ios',
- attempts: 1,
- status: Task.statusInProgress,
- isFlaky: true, // mark flaky so it doesn't get auto-retried
- commitKey: commit.key,
- );
- config.db.values[commit.key] = commit;
- config.db.values[task.key] = task;
- tester.requestData = <String, dynamic>{
- UpdateTaskStatus.gitBranchParam: 'master',
- UpdateTaskStatus.gitShaParam: commitSha,
- UpdateTaskStatus.newStatusParam: 'Failed',
- UpdateTaskStatus.builderNameParam: 'linux_integration_ui_ios',
- };
-
- await tester.post(handler);
-
- expect(task.status, 'Failed');
- expect(task.attempts, 1);
- });
-
- test('task name requests when task does not exists returns exception', () async {
- tester.requestData = <String, dynamic>{
- UpdateTaskStatus.gitBranchParam: 'master',
- UpdateTaskStatus.gitShaParam: commitSha,
- UpdateTaskStatus.newStatusParam: 'Failed',
- UpdateTaskStatus.builderNameParam: 'linux_integration_ui_ios',
- };
- expect(tester.post(handler), throwsA(isA<KeyNotFoundException>()));
- });
-
- test('task name request updates when input has whitespace', () async {
- config.db.values[commit.key] = commit;
- final Task cocoonTask = Task(
- key: commit.key.append(Task, id: taskId),
- name: 'integration_ui_ios',
- attempts: 0,
- isFlaky: true, // mark flaky so it doesn't get auto-retried
- commitKey: commit.key,
- status: Task.statusNew,
- );
- config.db.values[cocoonTask.key] = cocoonTask;
- final Task luciTask = Task(
- key: commit.key.append(Task, id: taskId),
- name: 'integration_ui_ios',
- builderName: 'linux_integration_ui_ios',
- attempts: 1,
- status: Task.statusInProgress,
- isFlaky: true, // mark flaky so it doesn't get auto-retried
- commitKey: commit.key,
- );
- config.db.values[luciTask.key] = luciTask;
- const int asciiLF = 10;
- final List<int> branchChars = List<int>.from('master'.codeUnits)..add(asciiLF);
- final List<int> shaChars = List<int>.from(commitSha.codeUnits)..add(asciiLF);
- tester.requestData = <String, dynamic>{
- UpdateTaskStatus.gitBranchParam: String.fromCharCodes(branchChars),
- UpdateTaskStatus.gitShaParam: String.fromCharCodes(shaChars),
- UpdateTaskStatus.newStatusParam: 'Failed',
- UpdateTaskStatus.builderNameParam: 'linux_integration_ui_ios',
- };
-
- await tester.post(handler);
-
- expect(luciTask.status, Task.statusFailed);
- expect(luciTask.attempts, 1);
-
- expect(cocoonTask.status, Task.statusNew);
- expect(cocoonTask.attempts, 0);
- });
- });
-}
diff --git a/app_dart/test/request_handlers/vacuum_github_commits_test.dart b/app_dart/test/request_handlers/vacuum_github_commits_test.dart
deleted file mode 100644
index 5747e41..0000000
--- a/app_dart/test/request_handlers/vacuum_github_commits_test.dart
+++ /dev/null
@@ -1,182 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:cocoon_service/src/model/appengine/commit.dart';
-import 'package:cocoon_service/src/request_handlers/vacuum_github_commits.dart';
-import 'package:cocoon_service/src/request_handling/body.dart';
-import 'package:cocoon_service/src/service/config.dart';
-import 'package:cocoon_service/src/service/datastore.dart';
-import 'package:gcloud/db.dart' as gcloud_db;
-import 'package:gcloud/db.dart';
-import 'package:github/github.dart';
-import 'package:googleapis/bigquery/v2.dart';
-import 'package:mockito/mockito.dart';
-import 'package:test/test.dart';
-
-import '../src/datastore/fake_config.dart';
-import '../src/datastore/fake_datastore.dart';
-import '../src/request_handling/api_request_handler_tester.dart';
-import '../src/request_handling/fake_authentication.dart';
-import '../src/service/fake_github_service.dart';
-import '../src/service/fake_scheduler.dart';
-import '../src/utilities/mocks.dart';
-
-void main() {
- group('VacuumGithubCommits', () {
- late FakeConfig config;
- FakeAuthenticationProvider auth;
- late FakeDatastoreDB db;
- FakeScheduler scheduler;
- late ApiRequestHandlerTester tester;
- late VacuumGithubCommits handler;
-
- late List<String> githubCommits;
- late int yieldedCommitCount;
-
- List<RepositoryCommit> commitList() {
- final List<RepositoryCommit> commits = <RepositoryCommit>[];
- for (String sha in githubCommits) {
- final User author = User()
- ..login = 'Username'
- ..avatarUrl = 'http://example.org/avatar.jpg';
- final GitCommitUser committer =
- GitCommitUser('Username', 'Username@abc.com', DateTime.fromMillisecondsSinceEpoch(int.parse(sha)));
- final GitCommit gitCommit = GitCommit()
- ..message = 'commit message'
- ..committer = committer;
- commits.add(
- RepositoryCommit()
- ..sha = sha
- ..author = author
- ..commit = gitCommit,
- );
- }
- return commits;
- }
-
- Commit shaToCommit(String sha, String branch, RepositorySlug slug) {
- return Commit(
- key: db.emptyKey.append(Commit, id: '${slug.fullName}/$branch/$sha'),
- repository: slug.fullName,
- sha: sha,
- branch: branch,
- timestamp: int.parse(sha),
- );
- }
-
- setUp(() {
- final MockRepositoriesService repositories = MockRepositoriesService();
- final FakeGithubService githubService = FakeGithubService();
- final MockTabledataResource tabledataResourceApi = MockTabledataResource();
- when(tabledataResourceApi.insertAll(any, any, any, any)).thenAnswer((_) async {
- return TableDataInsertAllResponse();
- });
-
- yieldedCommitCount = 0;
- db = FakeDatastoreDB();
- config = FakeConfig(
- tabledataResource: tabledataResourceApi,
- githubService: githubService,
- dbValue: db,
- supportedBranchesValue: <String>[
- 'master',
- 'main',
- ],
- supportedReposValue: <RepositorySlug>{
- Config.cocoonSlug,
- Config.engineSlug,
- Config.flutterSlug,
- Config.packagesSlug,
- },
- );
-
- auth = FakeAuthenticationProvider();
- scheduler = FakeScheduler(
- config: config,
- ciYaml: exampleConfig,
- );
- tester = ApiRequestHandlerTester();
- handler = VacuumGithubCommits(
- config: config,
- authenticationProvider: auth,
- datastoreProvider: (DatastoreDB db) => DatastoreService(config.db, 5),
- scheduler: scheduler,
- );
-
- githubService.listCommitsBranch = (String branch, int hours) {
- return commitList();
- };
-
- when(githubService.github.repositories).thenReturn(repositories);
- });
-
- test('succeeds when GitHub returns no commits', () async {
- githubCommits = <String>[];
- config.supportedBranchesValue = <String>['master'];
- final Body body = await tester.get<Body>(handler);
- expect(yieldedCommitCount, 0);
- expect(db.values, isEmpty);
- expect(await body.serialize().toList(), isEmpty);
- });
-
- test('does not fail on empty commit list', () async {
- githubCommits = <String>[];
- expect(db.values.values.whereType<Commit>().length, 0);
- await tester.get<Body>(handler);
- expect(db.values.values.whereType<Commit>().length, 0);
- });
-
- test('does not add recent commits', () async {
- githubCommits = <String>['${DateTime.now().millisecondsSinceEpoch}'];
-
- expect(db.values.values.whereType<Commit>().length, 0);
- await tester.get<Body>(handler);
- expect(db.values.values.whereType<Commit>().length, 0);
- });
-
- test('inserts all relevant fields of the commit', () async {
- githubCommits = <String>['1'];
- expect(db.values.values.whereType<Commit>().length, 0);
- await tester.get<Body>(handler);
- expect(db.values.values.whereType<Commit>().length, config.supportedRepos.length);
- final List<Commit> commits = db.values.values.whereType<Commit>().toList();
- final Commit commit = commits.first;
- expect(commit.repository, 'flutter/cocoon');
- expect(commit.branch, 'main');
- expect(commit.sha, '1');
- expect(commit.timestamp, 1);
- expect(commit.author, 'Username');
- expect(commit.authorAvatarUrl, 'http://example.org/avatar.jpg');
- expect(commit.message, 'commit message');
- expect(commits[1].repository, Config.engineSlug.fullName);
- expect(commits[2].repository, Config.flutterSlug.fullName);
- });
-
- test('skips commits for which transaction commit fails', () async {
- githubCommits = <String>['2', '3', '4'];
-
- /// This test is simulating an existing branch, which must already
- /// have at least one commit in the datastore.
- final Commit commit = shaToCommit('1', 'main', Config.engineSlug);
- db.values[commit.key] = commit;
-
- db.onCommit = (List<gcloud_db.Model<dynamic>> inserts, List<gcloud_db.Key<dynamic>> deletes) {
- if (inserts.whereType<Commit>().where((Commit commit) => commit.sha == '3').isNotEmpty) {
- throw StateError('Commit failed');
- }
- };
- final Body body = await tester.get<Body>(handler);
-
- /// The +1 is coming from the engine repository and manually added commit on the top of this test.
- expect(db.values.values.whereType<Commit>().length, 8 + 1); // 2 commits for 4 repos
- expect(db.values.values.whereType<Commit>().map<String>(toSha), containsAll(<String>['1', '2', '4']));
- expect(db.values.values.whereType<Commit>().map<int>(toTimestamp), containsAll(<int>[1, 2, 4]));
- expect(await body.serialize().toList(), isEmpty);
- });
- });
-}
-
-String toSha(Commit commit) => commit.sha!;
-
-int toTimestamp(Commit commit) => commit.timestamp!;
diff --git a/app_dart/test/request_handling/api_request_handler_test.dart b/app_dart/test/request_handling/api_request_handler_test.dart
deleted file mode 100644
index fd218ee..0000000
--- a/app_dart/test/request_handling/api_request_handler_test.dart
+++ /dev/null
@@ -1,177 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-import 'dart:convert';
-import 'dart:io';
-
-import 'package:cocoon_service/src/request_handling/api_request_handler.dart';
-import 'package:cocoon_service/src/request_handling/body.dart';
-import 'package:cocoon_service/src/service/logging.dart';
-import 'package:gcloud/service_scope.dart' as ss;
-import 'package:logging/logging.dart';
-import 'package:test/test.dart';
-
-import '../src/datastore/fake_config.dart';
-import '../src/request_handling/fake_authentication.dart';
-
-void main() {
- group('ApiRequestHandler', () {
- late HttpServer server;
- late ApiRequestHandler<dynamic> handler;
-
- final List<LogRecord> records = <LogRecord>[];
-
- setUpAll(() async {
- server = await HttpServer.bind(InternetAddress.loopbackIPv4, 0);
- server.listen((HttpRequest request) {
- final ZoneSpecification spec = ZoneSpecification(
- print: (Zone self, ZoneDelegate parent, Zone zone, String line) {
- log.fine(line);
- },
- );
- runZoned<dynamic>(
- () {
- return ss.fork(() {
- ss.register(#appengine.logging, log);
- return handler.service(request);
- });
- },
- zoneSpecification: spec,
- );
- });
- });
-
- tearDownAll(() async {
- await server.close();
- });
-
- setUp(() {
- records.clear();
- log.onRecord.listen((LogRecord record) => records.add(record));
- });
-
- Future<HttpClientResponse> issueRequest({String? body}) async {
- final HttpClient client = HttpClient();
- final Uri url = Uri(scheme: 'http', host: 'localhost', port: server.port, path: '/path');
- final HttpClientRequest request = await client.getUrl(url);
- if (body != null) {
- request.contentLength = body.length;
- request.write(body);
- await request.flush();
- }
- return request.close();
- }
-
- test('failed authentication yields HTTP unauthorized', () async {
- handler = Unauth();
- final HttpClientResponse response = await issueRequest();
- expect(response.statusCode, HttpStatus.unauthorized);
- expect(await utf8.decoder.bind(response).join(), 'Not authenticated');
- expect(records, isEmpty);
- });
-
- test('empty request body yields empty requestData', () async {
- handler = ReadParams();
- final HttpClientResponse response = await issueRequest();
- expect(response.headers.value('X-Test-RequestBody'), '[]');
- expect(response.headers.value('X-Test-RequestData'), '{}');
- expect(response.statusCode, HttpStatus.ok);
- expect(await response.toList(), isEmpty);
- expect(records, isEmpty);
- });
-
- test('JSON request body yields valid requestData', () async {
- handler = ReadParams();
- final HttpClientResponse response = await issueRequest(body: '{"param1":"value1"}');
- expect(response.headers.value('X-Test-RequestBody'), isNotEmpty);
- expect(response.headers.value('X-Test-RequestData'), '{param1: value1}');
- expect(response.statusCode, HttpStatus.ok);
- expect(await response.toList(), isEmpty);
- expect(records, isEmpty);
- });
-
- test('non-JSON request body yields HTTP ok', () async {
- handler = ReadParams();
- final HttpClientResponse response = await issueRequest(body: 'abc');
- expect(response.statusCode, HttpStatus.ok);
- expect(response.headers.value('X-Test-RequestBody'), '[97, 98, 99]');
- expect(response.headers.value('X-Test-RequestData'), '{}');
- expect(await response.toList(), isEmpty);
- expect(records, isEmpty);
- });
-
- test('can access authContext', () async {
- handler = AccessAuth();
- final HttpClientResponse response = await issueRequest();
- expect(response.headers.value('X-Test-IsDev'), 'true');
- expect(response.statusCode, HttpStatus.ok);
- expect(records, isEmpty);
- });
-
- test('missing required request parameters yields HTTP bad request', () async {
- handler = NeedsParams();
- HttpClientResponse response = await issueRequest();
- expect(response.statusCode, HttpStatus.badRequest);
- response = await issueRequest(body: '{"param1":"value1"}');
- expect(response.statusCode, HttpStatus.badRequest);
- response = await issueRequest(body: '{"param2":"value2"}');
- expect(response.statusCode, HttpStatus.badRequest);
- response = await issueRequest(body: '{"param1":"value1","param2":"value2"}');
- expect(response.statusCode, HttpStatus.ok);
- response = await issueRequest(body: '{"param1":"value1","param2":"value2","extra":"yes"}');
- expect(response.statusCode, HttpStatus.ok);
- expect(records, isEmpty);
- });
- });
-}
-
-class Unauth extends ApiRequestHandler<Body> {
- Unauth()
- : super(
- config: FakeConfig(),
- authenticationProvider: FakeAuthenticationProvider(authenticated: false),
- );
-
- @override
- Future<Body> get() async => throw StateError('Unreachable');
-}
-
-class Auth extends ApiRequestHandler<Body> {
- Auth() : super(config: FakeConfig(), authenticationProvider: FakeAuthenticationProvider());
-
- @override
- Future<Body> get() async => Body.empty;
-}
-
-class ReadParams extends ApiRequestHandler<Body> {
- ReadParams() : super(config: FakeConfig(), authenticationProvider: FakeAuthenticationProvider());
-
- @override
- Future<Body> get() async {
- response!.headers.add('X-Test-RequestBody', requestBody.toString());
- response!.headers.add('X-Test-RequestData', requestData.toString());
- return Body.empty;
- }
-}
-
-class NeedsParams extends ApiRequestHandler<Body> {
- NeedsParams() : super(config: FakeConfig(), authenticationProvider: FakeAuthenticationProvider());
-
- @override
- Future<Body> get() async {
- checkRequiredParameters(<String>['param1', 'param2']);
- return Body.empty;
- }
-}
-
-class AccessAuth extends ApiRequestHandler<Body> {
- AccessAuth() : super(config: FakeConfig(), authenticationProvider: FakeAuthenticationProvider());
-
- @override
- Future<Body> get() async {
- response!.headers.add('X-Test-IsDev', authContext!.clientContext.isDevelopmentEnvironment);
- return Body.empty;
- }
-}
diff --git a/app_dart/test/request_handling/authentication_test.dart b/app_dart/test/request_handling/authentication_test.dart
deleted file mode 100644
index 20a375f..0000000
--- a/app_dart/test/request_handling/authentication_test.dart
+++ /dev/null
@@ -1,173 +0,0 @@
-// Copyright 2019 The Flutter 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:cocoon_service/src/model/appengine/allowed_account.dart';
-import 'package:cocoon_service/src/model/google/token_info.dart';
-import 'package:cocoon_service/src/request_handling/authentication.dart';
-import 'package:cocoon_service/src/request_handling/exceptions.dart';
-import 'package:cocoon_service/src/service/logging.dart';
-import 'package:http/http.dart' as http;
-import 'package:http/testing.dart';
-import 'package:logging/logging.dart';
-import 'package:test/test.dart';
-
-import '../src/datastore/fake_config.dart';
-import '../src/request_handling/fake_authentication.dart';
-import '../src/request_handling/fake_http.dart';
-
-void main() {
- group('AuthenticationProvider', () {
- late FakeConfig config;
- late FakeClientContext clientContext;
- late FakeHttpRequest request;
- late AuthenticationProvider auth;
- late TokenInfo token;
-
- setUp(() {
- config = FakeConfig();
- token = TokenInfo(email: 'abc123@gmail.com', issued: DateTime.now());
- clientContext = FakeClientContext();
- request = FakeHttpRequest();
- auth = AuthenticationProvider(
- config: config,
- clientContextProvider: () => clientContext,
- httpClientProvider: () => throw AssertionError(),
- );
- });
-
- test('throws Unauthenticated with no auth headers', () async {
- expect(auth.authenticate(request), throwsA(isA<Unauthenticated>()));
- });
-
- test('succeeds for App Engine cronjobs', () async {
- request.headers.set('X-Appengine-Cron', 'true');
- final AuthenticatedContext result = await auth.authenticate(request);
- expect(result.clientContext, same(clientContext));
- });
-
- group('when id token is given', () {
- late MockClient httpClient;
-
- setUp(() {
- auth = AuthenticationProvider(
- config: config,
- clientContextProvider: () => clientContext,
- httpClientProvider: () => httpClient,
- );
- });
-
- test('auth succeeds with authenticated header', () async {
- httpClient = MockClient((_) async => http.Response('{"aud": "client-id", "hd": "google.com"}', HttpStatus.ok));
- auth = AuthenticationProvider(
- config: config,
- clientContextProvider: () => clientContext,
- httpClientProvider: () => httpClient,
- );
- config.oauthClientIdValue = 'client-id';
- request.headers.add('X-Flutter-IdToken', 'authenticated');
- final AuthenticatedContext result = await auth.authenticate(request);
- expect(result.clientContext, same(clientContext));
- expect(result, isNotNull);
- });
-
- test('fails if token verification fails', () async {
- config.oauthClientIdValue = 'client-id';
- request.headers.add('X-Flutter-IdToken', 'authenticated');
- await expectLater(
- auth.authenticateToken(
- token,
- clientContext: FakeClientContext(),
- ),
- throwsA(isA<Unauthenticated>()),
- );
- });
-
- test('fails if tokenInfo returns invalid JSON', () async {
- httpClient = MockClient((_) async => http.Response('Not JSON!', HttpStatus.ok));
- final List<LogRecord> records = <LogRecord>[];
- log.onRecord.listen((LogRecord record) => records.add(record));
- await expectLater(auth.tokenInfo(request), throwsA(isA<InternalServerError>()));
- expect(records, isEmpty);
- });
-
- test('fails if token verification yields forged token', () async {
- final TokenInfo token = TokenInfo(
- audience: 'forgery',
- email: 'abc@abc.com',
- issued: DateTime.now(),
- );
- config.oauthClientIdValue = 'expected-client-id';
- await expectLater(
- auth.authenticateToken(
- token,
- clientContext: FakeClientContext(),
- ),
- throwsA(isA<Unauthenticated>()),
- );
- });
-
- test('allows different aud for gcloud tokens with google accounts', () async {
- final TokenInfo token = TokenInfo(
- audience: 'different',
- email: 'abc@google.com',
- issued: DateTime.now(),
- );
- config.oauthClientIdValue = 'expected-client-id';
- await expectLater(
- auth.authenticateToken(
- token,
- clientContext: FakeClientContext(),
- ),
- throwsA(isA<Unauthenticated>()),
- );
- });
-
- test('succeeds for google.com auth user', () async {
- final TokenInfo token = TokenInfo(
- audience: 'client-id',
- hostedDomain: 'google.com',
- email: 'abc@google.com',
- issued: DateTime.now(),
- );
- config.oauthClientIdValue = 'client-id';
- final AuthenticatedContext result = await auth.authenticateToken(
- token,
- clientContext: clientContext,
- );
- expect(result.clientContext, same(clientContext));
- });
-
- test('fails for non-allowed non-Google auth users', () async {
- final TokenInfo token =
- TokenInfo(audience: 'client-id', hostedDomain: 'gmail.com', email: 'abc@gmail.com', issued: DateTime.now());
- config.oauthClientIdValue = 'client-id';
- await expectLater(
- auth.authenticateToken(
- token,
- clientContext: FakeClientContext(),
- ),
- throwsA(isA<Unauthenticated>()),
- );
- });
-
- test('succeeds for allowed non-Google auth users', () async {
- final AllowedAccount account = AllowedAccount(
- key: config.db.emptyKey.append<int>(AllowedAccount, id: 123),
- email: 'test@gmail.com',
- );
- config.db.values[account.key] = account;
- final TokenInfo token = TokenInfo(
- audience: 'client-id',
- email: 'test@gmail.com',
- issued: DateTime.now(),
- );
- config.oauthClientIdValue = 'client-id';
- final AuthenticatedContext result = await auth.authenticateToken(token, clientContext: clientContext);
- expect(result.clientContext, same(clientContext));
- });
- });
- });
-}
diff --git a/app_dart/test/request_handling/cache_request_handler_test.dart b/app_dart/test/request_handling/cache_request_handler_test.dart
deleted file mode 100644
index 35484ae..0000000
--- a/app_dart/test/request_handling/cache_request_handler_test.dart
+++ /dev/null
@@ -1,108 +0,0 @@
-// Copyright 2019 The Flutter 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:convert';
-import 'dart:typed_data';
-
-import 'package:cocoon_service/src/request_handling/body.dart';
-import 'package:cocoon_service/src/request_handling/cache_request_handler.dart';
-import 'package:cocoon_service/src/service/cache_service.dart';
-import 'package:test/test.dart';
-
-import '../src/datastore/fake_config.dart';
-import '../src/request_handling/fake_http.dart';
-import '../src/request_handling/fake_request_handler.dart';
-import '../src/request_handling/request_handler_tester.dart';
-
-void main() {
- group('CacheRequestHandler', () {
- late FakeConfig config;
- late RequestHandlerTester tester;
-
- late CacheService cache;
-
- const String testHttpPath = '/cache_request_handler_test';
-
- setUp(() async {
- config = FakeConfig();
- tester = RequestHandlerTester(
- request: FakeHttpRequest(
- path: testHttpPath,
- ),
- );
-
- cache = CacheService(inMemory: true);
- });
-
- test('returns response from cache', () async {
- const String responseKey = '$testHttpPath:';
- const String expectedResponse = 'Hello, World!';
- final Body expectedBody = Body.forString(expectedResponse);
- final FakeRequestHandler fallbackHandler = FakeRequestHandler(body: expectedBody, config: FakeConfig());
-
- final Uint8List? serializedBody = await expectedBody.serialize().first;
-
- await cache.set(CacheRequestHandler.responseSubcacheName, responseKey, serializedBody);
-
- final CacheRequestHandler<Body> cacheRequestHandler =
- CacheRequestHandler<Body>(delegate: fallbackHandler, cache: cache, config: config);
-
- final Body body = await tester.get(cacheRequestHandler);
- final Uint8List response = (await body.serialize().first)!;
- final String strResponse = utf8.decode(response);
- expect(strResponse, expectedResponse);
- });
-
- test('fallback handler called when cache is empty', () async {
- final FakeRequestHandler fallbackHandler = FakeRequestHandler(
- body: Body.forString('hello!'),
- config: FakeConfig(),
- );
-
- final CacheRequestHandler<Body> cacheRequestHandler =
- CacheRequestHandler<Body>(delegate: fallbackHandler, cache: cache, config: config);
-
- expect(fallbackHandler.callCount, 0);
- await tester.get(cacheRequestHandler);
- expect(fallbackHandler.callCount, 1);
- });
-
- test('flush cache param calls purge', () async {
- tester = RequestHandlerTester(
- request: FakeHttpRequest(
- path: testHttpPath,
- queryParametersValue: <String, String>{
- CacheRequestHandler.flushCacheQueryParam: 'true',
- },
- ),
- );
- final FakeRequestHandler fallbackHandler = FakeRequestHandler(
- body: Body.empty,
- config: FakeConfig(),
- );
-
- const String responseKey = '$testHttpPath:';
- const String expectedResponse = 'Hello, World!';
- final Body expectedBody = Body.forString(expectedResponse);
-
- final Uint8List? serializedBody = await expectedBody.serialize().first;
-
- // set an existing response for the request
- await cache.set(CacheRequestHandler.responseSubcacheName, responseKey, serializedBody);
-
- final CacheRequestHandler<Body> cacheRequestHandler =
- CacheRequestHandler<Body>(delegate: fallbackHandler, cache: cache, config: config);
-
- expect(fallbackHandler.callCount, 0);
-
- final Body body = await tester.get(cacheRequestHandler);
- final Uint8List response = (await body.serialize().first)!;
- final String strResponse = utf8.decode(response);
-
- // the mock should update the cache to have null -> empty string
- expect(strResponse, '');
- expect(fallbackHandler.callCount, 1);
- });
- });
-}
diff --git a/app_dart/test/request_handling/pubsub_authentication_test.dart b/app_dart/test/request_handling/pubsub_authentication_test.dart
deleted file mode 100644
index ba1170a..0000000
--- a/app_dart/test/request_handling/pubsub_authentication_test.dart
+++ /dev/null
@@ -1,145 +0,0 @@
-// Copyright 2019 The Flutter 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:cocoon_service/src/request_handling/authentication.dart' show AuthenticatedContext;
-import 'package:cocoon_service/src/request_handling/exceptions.dart';
-import 'package:cocoon_service/src/request_handling/pubsub_authentication.dart';
-import 'package:cocoon_service/src/service/config.dart';
-import 'package:http/http.dart' as http;
-import 'package:http/testing.dart';
-import 'package:test/test.dart';
-
-import '../src/datastore/fake_config.dart';
-import '../src/request_handling/fake_authentication.dart';
-import '../src/request_handling/fake_http.dart';
-
-void main() {
- group('PubsubAuthenticationProvider', () {
- late FakeConfig config;
- late FakeClientContext clientContext;
- late FakeHttpRequest request;
- late PubsubAuthenticationProvider auth;
- late MockClient httpClient;
-
- setUp(() {
- config = FakeConfig();
- clientContext = FakeClientContext();
- request = FakeHttpRequest();
- auth = PubsubAuthenticationProvider(
- config: config,
- clientContextProvider: () => clientContext,
- httpClientProvider: () => httpClient,
- );
- });
-
- for (String allowedAccount in Config.allowedPubsubServiceAccounts) {
- test('auth succeeds for $allowedAccount', () async {
- httpClient = MockClient(
- (_) async => http.Response(
- _generateTokenResponse(allowedAccount),
- HttpStatus.ok,
- headers: <String, String>{
- HttpHeaders.contentTypeHeader: 'application/json',
- },
- ),
- );
- auth = PubsubAuthenticationProvider(
- config: config,
- clientContextProvider: () => clientContext,
- httpClientProvider: () => httpClient,
- );
-
- request.headers.add(HttpHeaders.authorizationHeader, 'Bearer token');
-
- final AuthenticatedContext result = await auth.authenticate(request);
- expect(result.clientContext, same(clientContext));
- });
- }
-
- test('auth fails with unauthorized service account', () async {
- httpClient = MockClient(
- (_) async => http.Response(
- _generateTokenResponse('unauthorized@gmail.com'),
- HttpStatus.ok,
- headers: <String, String>{
- HttpHeaders.contentTypeHeader: 'application/json',
- },
- ),
- );
- auth = PubsubAuthenticationProvider(
- config: config,
- clientContextProvider: () => clientContext,
- httpClientProvider: () => httpClient,
- );
-
- request.headers.add(HttpHeaders.authorizationHeader, 'Bearer token');
-
- expect(auth.authenticate(request), throwsA(isA<Unauthenticated>()));
- });
-
- test('auth fails with invalid token', () async {
- httpClient = MockClient(
- (_) async => http.Response(
- 'Invalid token',
- HttpStatus.unauthorized,
- headers: <String, String>{
- HttpHeaders.contentTypeHeader: 'application/json',
- },
- ),
- );
- auth = PubsubAuthenticationProvider(
- config: config,
- clientContextProvider: () => clientContext,
- httpClientProvider: () => httpClient,
- );
-
- request.headers.add(HttpHeaders.authorizationHeader, 'Bearer token');
-
- expect(auth.authenticate(request), throwsA(isA<FormatException>()));
- });
-
- test('auth fails with expired token', () async {
- httpClient = MockClient(
- (_) async => http.Response(
- _generateTokenResponse(
- Config.allowedPubsubServiceAccounts.first,
- expiresIn: -1,
- ),
- HttpStatus.ok,
- headers: <String, String>{
- HttpHeaders.contentTypeHeader: 'application/json',
- },
- ),
- );
- auth = PubsubAuthenticationProvider(
- config: config,
- clientContextProvider: () => clientContext,
- httpClientProvider: () => httpClient,
- );
-
- request.headers.add(HttpHeaders.authorizationHeader, 'Bearer token');
-
- expect(auth.authenticate(request), throwsA(isA<Unauthenticated>()));
- });
- });
-}
-
-/// Return Google's OAuth response.
-String _generateTokenResponse(
- String email, {
- int expiresIn = 123,
-}) {
- return '''{
- "issued_to": "456",
- "audience": "https://flutter-dashboard.appspot.com/api/luci-status-handler",
- "user_id": "789",
- "expires_in": $expiresIn,
- "email": "$email",
- "verified_email": true,
- "issuer": "https://accounts.google.com",
- "issued_at": 412321
- }''';
-}
diff --git a/app_dart/test/request_handling/request_handler_test.dart b/app_dart/test/request_handling/request_handler_test.dart
deleted file mode 100644
index 269df14..0000000
--- a/app_dart/test/request_handling/request_handler_test.dart
+++ /dev/null
@@ -1,212 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-import 'dart:convert';
-import 'dart:io';
-
-import 'package:cocoon_service/src/request_handling/body.dart';
-import 'package:cocoon_service/src/request_handling/exceptions.dart';
-import 'package:cocoon_service/src/request_handling/request_handler.dart';
-import 'package:cocoon_service/src/service/logging.dart';
-import 'package:gcloud/service_scope.dart' as ss;
-import 'package:logging/logging.dart';
-import 'package:test/test.dart';
-
-import '../src/datastore/fake_config.dart';
-
-void main() {
- group('RequestHandler', () {
- late HttpServer server;
- late RequestHandler<dynamic> handler;
-
- final List<LogRecord> records = <LogRecord>[];
-
- setUpAll(() async {
- server = await HttpServer.bind(InternetAddress.loopbackIPv4, 0);
- server.listen((HttpRequest request) {
- runZoned<dynamic>(() {
- return ss.fork(() {
- return handler.service(request);
- });
- });
- });
- });
-
- tearDownAll(() async {
- await server.close();
- });
-
- setUp(() {
- records.clear();
- log.onRecord.listen((LogRecord record) => records.add(record));
- });
-
- Future<HttpClientResponse> issueRequest(String method) async {
- final HttpClient client = HttpClient();
- final Uri url = Uri(scheme: 'http', host: 'localhost', port: server.port, path: '/path');
- final HttpClientRequest request = await client.openUrl(method, url);
- return request.close();
- }
-
- Future<HttpClientResponse> issueGet() => issueRequest('get');
-
- Future<HttpClientResponse> issuePost() => issueRequest('post');
-
- test('Unimplemented methods yield HTTP method not allowed', () async {
- handler = MethodNotAllowed();
- HttpClientResponse response = await issueGet();
- expect(response.statusCode, HttpStatus.methodNotAllowed);
- response = await issuePost();
- expect(response.statusCode, HttpStatus.methodNotAllowed);
- expect(records, isEmpty);
- });
-
- test('empty body yields empty HTTP response body', () async {
- handler = EmptyBodyHandler();
- final HttpClientResponse response = await issueGet();
- expect(response.statusCode, HttpStatus.ok);
- expect(await response.toList(), isEmpty);
- expect(records, isEmpty);
- });
-
- test('string body yields string HTTP response body', () async {
- handler = StringBodyHandler();
- final HttpClientResponse response = await issueGet();
- expect(response.statusCode, HttpStatus.ok);
- expect(await utf8.decoder.bind(response).join(), 'Hello world');
- expect(records, isEmpty);
- });
-
- test('JsonBody yields JSON HTTP response body', () async {
- handler = JsonBodyHandler();
- final HttpClientResponse response = await issueGet();
- expect(response.statusCode, HttpStatus.ok);
- expect(await utf8.decoder.bind(response).join(), '{"key":"value"}');
- expect(records, isEmpty);
- });
-
- test('throwing HttpException yields corresponding HTTP status', () async {
- handler = ThrowsHttpException();
- final HttpClientResponse response = await issueGet();
- expect(response.statusCode, HttpStatus.badRequest);
- expect(await utf8.decoder.bind(response).join(), 'Bad request');
- expect(records, isEmpty);
- });
-
- test('throwing general exception yields HTTP 500 and logs to server logs', () async {
- handler = ThrowsStateError();
- final HttpClientResponse response = await issueGet();
- expect(response.statusCode, HttpStatus.internalServerError);
- expect(await utf8.decoder.bind(response).join(), contains('error message'));
- expect(records.first.message, contains('error message'));
- });
-
- test('may access the request and response directly', () async {
- handler = AccessesRequestAndResponseDirectly();
- final HttpClientResponse response = await issueGet();
- expect(response.headers.value('X-Test-Path'), '/path');
- expect(records, isEmpty);
- });
-
- test('may implement both GET and POST', () async {
- handler = ImplementsBothGetAndPost();
- HttpClientResponse response = await issueGet();
- expect(response.headers.value('X-Test-Get'), 'true');
- expect(response.headers.value('X-Test-Post'), isNull);
- response = await issuePost();
- expect(response.headers.value('X-Test-Get'), isNull);
- expect(response.headers.value('X-Test-Post'), 'true');
- expect(records, isEmpty);
- });
-
- test('may implement only POST', () async {
- handler = ImplementsOnlyPost();
- HttpClientResponse response = await issueGet();
- expect(response.statusCode, HttpStatus.methodNotAllowed);
- response = await issuePost();
- expect(response.statusCode, HttpStatus.ok);
- expect(records, isEmpty);
- });
- });
-}
-
-class TestBody extends JsonBody {
- const TestBody();
-
- @override
- Map<String, dynamic> toJson() => const <String, dynamic>{'key': 'value'};
-}
-
-class MethodNotAllowed extends RequestHandler<Body> {
- MethodNotAllowed() : super(config: FakeConfig());
-}
-
-class EmptyBodyHandler extends RequestHandler<Body> {
- EmptyBodyHandler() : super(config: FakeConfig());
-
- @override
- Future<Body> get() async => Body.empty;
-}
-
-class StringBodyHandler extends RequestHandler<Body> {
- StringBodyHandler() : super(config: FakeConfig());
-
- @override
- Future<Body> get() async => Body.forString('Hello world');
-}
-
-class JsonBodyHandler extends RequestHandler<TestBody> {
- JsonBodyHandler() : super(config: FakeConfig());
-
- @override
- Future<TestBody> get() async => const TestBody();
-}
-
-class ThrowsHttpException extends RequestHandler<Body> {
- ThrowsHttpException() : super(config: FakeConfig());
-
- @override
- Future<Body> get() async => throw const BadRequestException();
-}
-
-class ThrowsStateError extends RequestHandler<Body> {
- ThrowsStateError() : super(config: FakeConfig());
-
- @override
- Future<Body> get() async => throw StateError('error message');
-}
-
-class AccessesRequestAndResponseDirectly extends RequestHandler<Body> {
- AccessesRequestAndResponseDirectly() : super(config: FakeConfig());
-
- @override
- Future<Body> get() async {
- response!.headers.add('X-Test-Path', request!.uri.path);
- return Body.empty;
- }
-}
-
-class ImplementsBothGetAndPost extends RequestHandler<Body> {
- ImplementsBothGetAndPost() : super(config: FakeConfig());
-
- @override
- Future<Body> get() async {
- response!.headers.add('X-Test-Get', 'true');
- return Body.empty;
- }
-
- @override
- Future<Body> post() async {
- response!.headers.add('X-Test-Post', 'true');
- return Body.empty;
- }
-}
-
-class ImplementsOnlyPost extends RequestHandler<Body> {
- ImplementsOnlyPost() : super(config: FakeConfig());
-
- @override
- Future<Body> post() async => Body.empty;
-}
diff --git a/app_dart/test/request_handling/static_file_handler_test.dart b/app_dart/test/request_handling/static_file_handler_test.dart
deleted file mode 100644
index 92cd310..0000000
--- a/app_dart/test/request_handling/static_file_handler_test.dart
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 2019 The Flutter 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:convert' show utf8;
-
-import 'package:cocoon_service/cocoon_service.dart';
-import 'package:cocoon_service/src/request_handling/exceptions.dart';
-import 'package:file/file.dart';
-import 'package:file/memory.dart';
-import 'package:test/test.dart';
-
-import '../src/datastore/fake_config.dart';
-import '../src/request_handling/request_handler_tester.dart';
-
-void main() {
- group('StaticFileHandler', () {
- late RequestHandlerTester tester;
- late FileSystem fs;
-
- final FakeConfig config = FakeConfig();
-
- const String indexFileName = 'index.html';
- const String indexFileContent = 'some html';
- const String dartMapFileName = 'main.dart.js.map';
- const String assetManifestSmcbin = 'AssetManifest.smcbin';
-
- setUp(() {
- tester = RequestHandlerTester();
- fs = MemoryFileSystem();
- fs.file('build/web/$indexFileName').createSync(recursive: true);
- fs.file('build/web/$indexFileName').writeAsStringSync(indexFileContent);
- fs.file('build/web/$assetManifestSmcbin').writeAsStringSync(assetManifestSmcbin);
- fs.file('build/web/$dartMapFileName').writeAsStringSync('[{}]');
- });
-
- Future<String> decodeHandlerBody(Body body) {
- return utf8.decoder.bind(body.serialize() as Stream<List<int>>).first;
- }
-
- test('returns 404 response when file does not exist', () async {
- final StaticFileHandler staticFileHandler = StaticFileHandler('i do not exist as a file', config: config, fs: fs);
-
- expect(tester.get(staticFileHandler), throwsA(const TypeMatcher<NotFoundException>()));
- });
-
- test('returns body when file does exist', () async {
- final StaticFileHandler staticFileHandler = StaticFileHandler('/$indexFileName', config: config, fs: fs);
-
- final Body body = await tester.get(staticFileHandler);
- expect(body, isNotNull);
- final String response = await decodeHandlerBody(body);
- expect(response, indexFileContent);
- });
-
- test('DartMap file does not raise exception', () async {
- final StaticFileHandler staticFileHandler = StaticFileHandler('/$dartMapFileName', config: config, fs: fs);
-
- final Body body = await tester.get(staticFileHandler);
- expect(body, isNotNull);
- final String response = await decodeHandlerBody(body);
- expect(response, '[{}]');
- });
-
- test('smcbin file extension is handled correctly', () async {
- final StaticFileHandler staticFileHandler = StaticFileHandler('/$assetManifestSmcbin', config: config, fs: fs);
-
- final Body body = await tester.get(staticFileHandler);
- expect(body, isNotNull);
- final String response = await decodeHandlerBody(body);
- expect(response, assetManifestSmcbin);
- });
-
- test('No extension files default to plain text', () async {
- fs.file('build/web/NOTICE').writeAsStringSync('abc');
- final StaticFileHandler staticFileHandler = StaticFileHandler('/NOTICE', config: config, fs: fs);
-
- final Body body = await tester.get(staticFileHandler);
- expect(body, isNotNull);
- final String response = await decodeHandlerBody(body);
- expect(response, 'abc');
- });
- });
-}
diff --git a/app_dart/test/request_handling/subscription_handler_test.dart b/app_dart/test/request_handling/subscription_handler_test.dart
deleted file mode 100644
index ce97bf8..0000000
--- a/app_dart/test/request_handling/subscription_handler_test.dart
+++ /dev/null
@@ -1,171 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-import 'dart:convert';
-import 'dart:io';
-import 'dart:typed_data';
-
-import 'package:cocoon_service/src/model/luci/push_message.dart';
-import 'package:cocoon_service/src/request_handling/body.dart';
-import 'package:cocoon_service/src/request_handling/exceptions.dart';
-import 'package:cocoon_service/src/request_handling/subscription_handler.dart';
-import 'package:cocoon_service/src/service/cache_service.dart';
-import 'package:gcloud/service_scope.dart' as ss;
-import 'package:test/test.dart';
-
-import '../src/datastore/fake_config.dart';
-import '../src/request_handling/fake_authentication.dart';
-
-void main() {
- group('Subscription', () {
- late HttpServer server;
- late SubscriptionHandler subscription;
-
- const PushMessageEnvelope testEnvelope = PushMessageEnvelope(
- message: PushMessage(
- data: 'test',
- messageId: '123',
- ),
- subscription: 'https://flutter-dashboard.appspot.com/api/luci-status-handler',
- );
-
- setUp(() async {
- server = await HttpServer.bind(InternetAddress.loopbackIPv4, 0);
- server.listen((HttpRequest request) {
- runZoned<dynamic>(
- () {
- return ss.fork(() {
- return subscription.service(request);
- });
- },
- );
- });
- });
-
- tearDown(() async {
- await server.close();
- });
-
- Future<HttpClientResponse> issueRequest({String? body}) async {
- final HttpClient client = HttpClient();
- final Uri url = Uri(scheme: 'http', host: 'localhost', port: server.port, path: '/path');
- final HttpClientRequest request = await client.getUrl(url);
- if (body != null) {
- request.contentLength = body.length;
- request.write(body);
- await request.flush();
- }
- return request.close();
- }
-
- test('failed authentication yields HTTP unauthorized', () async {
- subscription = UnauthTest();
- final HttpClientResponse response = await issueRequest();
- expect(response.statusCode, HttpStatus.unauthorized);
- expect(await utf8.decoder.bind(response).join(), 'Not authenticated');
- });
-
- test('passed authentication yields empty body', () async {
- subscription = AuthTest();
- final HttpClientResponse response = await issueRequest(body: jsonEncode(testEnvelope));
- expect(response.statusCode, HttpStatus.ok);
- });
-
- test('pubsub message is parsed', () async {
- subscription = ReadMessageTest();
- final HttpClientResponse response = await issueRequest(body: jsonEncode(testEnvelope));
- expect(response.statusCode, HttpStatus.ok);
- final String responseBody = String.fromCharCodes((await response.toList()).first);
- expect(responseBody, 'test');
- });
-
- test('ensure message ids are idempotent', () async {
- final CacheService cache = CacheService(inMemory: true);
- subscription = ReadMessageTest(cache);
- HttpClientResponse response = await issueRequest(body: jsonEncode(testEnvelope));
- String responseBody = String.fromCharCodes((await response.toList()).first);
- expect(response.statusCode, HttpStatus.ok);
- // 1. Expected message for this was processed
- expect(responseBody, 'test');
-
- response = await issueRequest(body: jsonEncode(testEnvelope));
- responseBody = String.fromCharCodes((await response.toList()).first);
- expect(response.statusCode, HttpStatus.ok);
- // 2. Empty message is returned as this was already processed
- expect(responseBody, '123 was already processed');
- expect(await cache.getOrCreate(subscription.subscriptionName, '123', createFn: null), isNotNull);
- });
-
- test('ensure messages can be retried', () async {
- final CacheService cache = CacheService(inMemory: true);
- subscription = ErrorTest(cache);
- HttpClientResponse response = await issueRequest(body: jsonEncode(testEnvelope));
- Uint8List? messageLock = await cache.getOrCreate('error', '123', createFn: null);
- expect(response.statusCode, HttpStatus.internalServerError);
- expect(messageLock, isNull);
-
- response = await issueRequest(body: jsonEncode(testEnvelope));
- messageLock = await cache.getOrCreate('error', '123', createFn: null);
- expect(response.statusCode, HttpStatus.internalServerError);
- expect(messageLock, isNull);
- });
- });
-}
-
-/// Test stub of [SubscriptionHandler] to validate unauthenticated requests.
-class UnauthTest extends SubscriptionHandler {
- UnauthTest()
- : super(
- cache: CacheService(inMemory: true),
- config: FakeConfig(),
- authProvider: FakeAuthenticationProvider(authenticated: false),
- subscriptionName: 'unauth',
- );
-
- @override
- Future<Body> get() async => throw StateError('Unreachable');
-}
-
-/// Test stub of [SubscriptionHandler] to validate authenticated requests.
-class AuthTest extends SubscriptionHandler {
- AuthTest()
- : super(
- cache: CacheService(inMemory: true),
- config: FakeConfig(),
- authProvider: FakeAuthenticationProvider(),
- subscriptionName: 'auth',
- );
-
- @override
- Future<Body> get() async => Body.empty;
-}
-
-/// Test stub of [SubscriptionHandler] to validate push messages can be read.
-class ErrorTest extends SubscriptionHandler {
- ErrorTest([CacheService? cache])
- : super(
- cache: cache ?? CacheService(inMemory: true),
- config: FakeConfig(),
- authProvider: FakeAuthenticationProvider(),
- subscriptionName: 'error',
- );
-
- @override
- Future<Body> get() async => throw const InternalServerError('Test error!');
-}
-
-/// Test stub of [SubscriptionHandler] to validate push messages can be read.
-class ReadMessageTest extends SubscriptionHandler {
- ReadMessageTest([CacheService? cache])
- : super(
- cache: cache ?? CacheService(inMemory: true),
- config: FakeConfig(),
- authProvider: FakeAuthenticationProvider(),
- subscriptionName: 'read',
- );
-
- @override
- Future<Body> get() async => Body.forString(message.data!);
-}
diff --git a/app_dart/test/request_handling/swarming_authentication_test.dart b/app_dart/test/request_handling/swarming_authentication_test.dart
deleted file mode 100644
index 80ce5f8..0000000
--- a/app_dart/test/request_handling/swarming_authentication_test.dart
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright 2019 The Flutter 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:cocoon_service/src/request_handling/authentication.dart' show AuthenticatedContext;
-import 'package:cocoon_service/src/request_handling/exceptions.dart';
-import 'package:cocoon_service/src/request_handling/swarming_authentication.dart';
-import 'package:cocoon_service/src/service/config.dart';
-import 'package:http/http.dart' as http;
-import 'package:http/testing.dart';
-import 'package:test/test.dart';
-
-import '../src/datastore/fake_config.dart';
-import '../src/request_handling/fake_authentication.dart';
-import '../src/request_handling/fake_http.dart';
-
-void main() {
- group('SwarmingAuthenticationProvider', () {
- late FakeConfig config;
- late FakeClientContext clientContext;
- late FakeHttpRequest request;
- late SwarmingAuthenticationProvider auth;
-
- setUp(() {
- config = FakeConfig();
- clientContext = FakeClientContext();
- request = FakeHttpRequest();
- auth = SwarmingAuthenticationProvider(
- config: config,
- clientContextProvider: () => clientContext,
- );
- });
-
- test('fails for App Engine cronjobs', () async {
- request.headers.set('X-Appengine-Cron', 'true');
- expect(auth.authenticate(request), throwsA(isA<Unauthenticated>()));
- });
-
- group('when access token is given', () {
- late MockClient httpClient;
-
- setUp(() {
- auth = SwarmingAuthenticationProvider(
- config: config,
- clientContextProvider: () => clientContext,
- httpClientProvider: () => httpClient,
- );
- });
-
- test('auth succeeds with flutter luci service account', () async {
- httpClient = MockClient((_) async => http.Response('{"email": "${Config.luciProdAccount}"}', HttpStatus.ok));
- auth = SwarmingAuthenticationProvider(
- config: config,
- clientContextProvider: () => clientContext,
- httpClientProvider: () => httpClient,
- );
-
- request.headers.add(SwarmingAuthenticationProvider.kSwarmingTokenHeader, 'token');
-
- final AuthenticatedContext result = await auth.authenticate(request);
- expect(result.clientContext, same(clientContext));
- });
-
- test('auth succeeds with frob service account', () async {
- httpClient = MockClient((_) async => http.Response('{"email": "${Config.frobAccount}"}', HttpStatus.ok));
- auth = SwarmingAuthenticationProvider(
- config: config,
- clientContextProvider: () => clientContext,
- httpClientProvider: () => httpClient,
- );
- request.headers.add(SwarmingAuthenticationProvider.kSwarmingTokenHeader, 'token');
-
- final AuthenticatedContext result = await auth.authenticate(request);
- expect(result.clientContext, same(clientContext));
- });
-
- test('auth fails with non-luci service account', () async {
- httpClient = MockClient((_) async => http.Response('{"email": "abc@gmail.com"}', HttpStatus.ok));
- auth = SwarmingAuthenticationProvider(
- config: config,
- clientContextProvider: () => clientContext,
- httpClientProvider: () => httpClient,
- );
-
- request.headers.add(SwarmingAuthenticationProvider.kSwarmingTokenHeader, 'unauthenticated token');
-
- expect(auth.authenticate(request), throwsA(isA<Unauthenticated>()));
- });
-
- test('auth fails with unauthenticated service account token', () async {
- httpClient = MockClient((_) async => http.Response('Invalid token', HttpStatus.unauthorized));
- auth = SwarmingAuthenticationProvider(
- config: config,
- clientContextProvider: () => clientContext,
- httpClientProvider: () => httpClient,
- );
-
- request.headers.add(SwarmingAuthenticationProvider.kSwarmingTokenHeader, 'unauthenticated token');
-
- expect(auth.authenticate(request), throwsA(isA<Unauthenticated>()));
- });
- });
- });
-}
diff --git a/app_dart/test/service/bigquery_test.dart b/app_dart/test/service/bigquery_test.dart
deleted file mode 100644
index 09f5387..0000000
--- a/app_dart/test/service/bigquery_test.dart
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright 2020 The Flutter 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:convert';
-
-import 'package:cocoon_service/src/service/bigquery.dart';
-
-import 'package:googleapis/bigquery/v2.dart';
-import 'package:mockito/mockito.dart';
-import 'package:test/test.dart';
-
-import '../src/service/fake_bigquery_service.dart';
-import '../src/utilities/mocks.dart';
-
-const String semanticsIntegrationTestResponse = '''
-{
- "jobComplete" : true,
- "rows": [
- { "f": [
- { "v": "Mac_android android_semantics_integration_test"},
- { "v": "1" },
- { "v": "2" },
- { "v": "101, 102, 103" },
- { "v": "201, 202, 203" },
- { "v": "abc" },
- { "v": "103" },
- { "v": "0.5"},
- {"v": "2023-06-20"},
- {"v": "2023-06-29"}
- ]
- }
- ]
-}
-''';
-
-const String noRecordsResponse = '''
-{
- "jobComplete" : true
-}
-''';
-
-const String jobNotCompleteResponse = '''
-{
- "jobComplete" : false
-}
-''';
-
-const String expectedProjectId = 'project-id';
-
-void main() {
- late FakeBigqueryService service;
- late MockJobsResource jobsResource;
- setUp(() {
- jobsResource = MockJobsResource();
- service = FakeBigqueryService(jobsResource);
- });
-
- test('can handle unsuccessful job query', () async {
- // When queries flaky data from BigQuery.
- when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) {
- return Future<QueryResponse>.value(
- QueryResponse.fromJson(jsonDecode(jobNotCompleteResponse) as Map<dynamic, dynamic>),
- );
- });
- bool hasError = false;
- try {
- await service.listBuilderStatistic(expectedProjectId);
- } catch (e) {
- expect(e.toString(), 'job does not complete');
- hasError = true;
- }
- expect(hasError, isTrue);
- });
-
- test('can handle job query', () async {
- // When queries flaky data from BigQuery.
- when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) {
- return Future<QueryResponse>.value(
- QueryResponse.fromJson(jsonDecode(semanticsIntegrationTestResponse) as Map<dynamic, dynamic>),
- );
- });
- final List<BuilderStatistic> statisticList = await service.listBuilderStatistic(expectedProjectId);
- expect(statisticList.length, 1);
- expect(statisticList[0].name, 'Mac_android android_semantics_integration_test');
- expect(statisticList[0].flakyRate, 0.5);
- expect(statisticList[0].succeededBuilds!.length, 3);
- expect(statisticList[0].succeededBuilds![0], '203');
- expect(statisticList[0].succeededBuilds![1], '202');
- expect(statisticList[0].succeededBuilds![2], '201');
- expect(statisticList[0].flakyBuilds!.length, 3);
- expect(statisticList[0].flakyBuilds![0], '103');
- expect(statisticList[0].flakyBuilds![1], '102');
- expect(statisticList[0].flakyBuilds![2], '101');
- expect(statisticList[0].recentCommit, 'abc');
- expect(statisticList[0].flakyBuildOfRecentCommit, '103');
- expect(statisticList[0].fromDate, '2023-06-20');
- expect(statisticList[0].toDate, '2023-06-29');
- });
-
- test('return empty build list when bigquery returns no rows', () async {
- when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) {
- return Future<QueryResponse>.value(
- QueryResponse.fromJson(jsonDecode(noRecordsResponse) as Map<dynamic, dynamic>),
- );
- });
- final List<BuilderRecord> records =
- await service.listRecentBuildRecordsForBuilder(expectedProjectId, builder: 'test', limit: 10);
- expect(records.length, 0);
- });
-}
diff --git a/app_dart/test/service/branch_service_test.dart b/app_dart/test/service/branch_service_test.dart
deleted file mode 100644
index 0be7925..0000000
--- a/app_dart/test/service/branch_service_test.dart
+++ /dev/null
@@ -1,172 +0,0 @@
-// Copyright 2021 The Flutter 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 'package:cocoon_service/src/model/gerrit/commit.dart';
-import 'package:cocoon_service/src/request_handling/exceptions.dart';
-import 'package:cocoon_service/src/service/branch_service.dart';
-import 'package:cocoon_service/src/service/config.dart';
-
-import 'package:github/github.dart' as gh;
-import 'package:mockito/mockito.dart';
-import 'package:retry/retry.dart';
-import 'package:test/test.dart';
-
-import '../src/datastore/fake_datastore.dart';
-import '../src/service/fake_gerrit_service.dart';
-import '../src/service/fake_github_service.dart';
-import '../src/utilities/entity_generators.dart';
-import '../src/utilities/matchers.dart';
-import '../src/utilities/mocks.mocks.dart';
-
-void main() {
- late MockConfig config;
- late FakeDatastoreDB db;
- late BranchService branchService;
- late FakeGerritService gerritService;
- late FakeGithubService githubService;
- late MockRepositoriesService repositories;
-
- setUp(() {
- db = FakeDatastoreDB();
- final MockGitHub github = MockGitHub();
- githubService = FakeGithubService(client: github);
- repositories = MockRepositoriesService();
- when(github.repositories).thenReturn(repositories);
- config = MockConfig();
- gerritService = FakeGerritService();
- branchService = BranchService(
- config: config,
- gerritService: gerritService,
- retryOptions: const RetryOptions(maxDelay: Duration.zero),
- );
-
- when(config.createDefaultGitHubService()).thenAnswer((_) async => githubService);
- when(config.db).thenReturn(db);
- });
-
- group('retrieve latest release branches', () {
- late MockRepositoriesService mockRepositoriesService;
-
- setUp(() {
- mockRepositoriesService = MockRepositoriesService();
- when(githubService.github.repositories).thenReturn(mockRepositoriesService);
- });
-
- test('return beta, stable, and latest candidate branches', () async {
- final gh.Branch stableBranch = generateBranch(1, name: 'flutter-2.13-candidate.0', sha: '123stable');
- final gh.Branch betaBranch = generateBranch(2, name: 'flutter-3.2-candidate.5', sha: '456beta');
- final gh.Branch candidateBranch = generateBranch(3, name: 'flutter-3.4-candidate.5', sha: '789dev');
- final gh.Branch candidateBranchOne = generateBranch(4, name: 'flutter-3.3-candidate.9', sha: 'lagerZValue');
- final gh.Branch candidateBranchTwo =
- generateBranch(5, name: 'flutter-2.15-candidate.99', sha: 'superLargeYZvalue');
- final gh.Branch candidateBranchThree = generateBranch(6, name: 'flutter-0.5-candidate.0', sha: 'someZeroValues');
- final gh.Branch candidateCherrypickBranch =
- generateBranch(6, name: 'cherry-picks-flutter-3.11-candidate.3', sha: 'bad');
-
- when(mockRepositoriesService.listBranches(any)).thenAnswer((Invocation invocation) {
- return Stream.fromIterable([
- candidateBranch,
- candidateBranchOne,
- stableBranch,
- betaBranch,
- candidateBranchTwo,
- candidateBranchThree,
- candidateCherrypickBranch,
- ]);
- });
- final List<Map<String, String>> result =
- await branchService.getReleaseBranches(githubService: githubService, slug: Config.flutterSlug);
- expect(result.length, 4);
- expect(result[1]['branch'], 'flutter-2.13-candidate.0');
- expect(result[1]['name'], 'stable');
- final devBranch = result.singleWhere((Map<String, String> branch) => branch['name'] == 'dev');
- expect(devBranch['branch'], 'flutter-3.4-candidate.5');
- });
- });
-
- group('branchFlutterRecipes', () {
- const String branch = 'flutter-2.13-candidate.0';
- const String sha = 'abc123';
- setUp(() {
- gerritService.branchesValue = <String>[];
- when(repositories.getCommit(Config.engineSlug, sha)).thenAnswer((_) async => generateGitCommit(5));
- });
-
- test('does not create branch that already exists', () async {
- gerritService.branchesValue = <String>[branch];
- expect(
- () async => branchService.branchFlutterRecipes(branch, sha),
- throwsExceptionWith<BadRequestException>('$branch already exists'),
- );
- });
-
- test('throws BadRequest if github commit has no branch time', () async {
- gerritService.commitsValue = <GerritCommit>[];
- when(repositories.getCommit(Config.engineSlug, sha)).thenAnswer(
- (_) async => gh.RepositoryCommit(
- commit: gh.GitCommit(
- committer: gh.GitCommitUser(
- 'dash',
- 'dash@flutter.dev',
- null,
- ),
- ),
- ),
- );
- expect(
- () async => branchService.branchFlutterRecipes(branch, sha),
- throwsExceptionWith<BadRequestException>('$sha has no commit time'),
- );
- });
-
- test('does not create branch if a good branch point cannot be found', () async {
- gerritService.commitsValue = <GerritCommit>[];
- when(repositories.getCommit(Config.engineSlug, sha)).thenAnswer(
- (_) async => generateGitCommit(5),
- );
- expect(
- () async => branchService.branchFlutterRecipes(branch, sha),
- throwsExceptionWith<InternalServerError>(
- 'HTTP 500: Failed to find a revision to flutter/recipes for $branch before 1969-12-31',
- ),
- );
- });
-
- test('creates branch', () async {
- await branchService.branchFlutterRecipes(branch, sha);
- });
-
- test('creates branch when GitHub requires retries', () async {
- int attempts = 0;
- when(repositories.getCommit(Config.engineSlug, sha)).thenAnswer((_) async {
- attempts++;
- if (attempts == 3) {
- return generateGitCommit(5);
- }
- throw gh.GitHubError(MockGitHub(), 'Failed to get commit');
- });
- await branchService.branchFlutterRecipes(branch, sha);
- });
-
- test('ensure createDefaultGithubService is called once for each retry', () async {
- int attempts = 0;
- when(repositories.getCommit(Config.engineSlug, sha)).thenAnswer((_) async {
- attempts++;
- if (attempts == 3) {
- return generateGitCommit(5);
- }
- throw gh.GitHubError(MockGitHub(), 'Failed to get commit');
- });
- await branchService.branchFlutterRecipes(branch, sha);
-
- verify(config.createDefaultGitHubService()).called(attempts);
- });
-
- test('creates branch when there is a similar branch', () async {
- gerritService.branchesValue = <String>['$branch-similar'];
-
- await branchService.branchFlutterRecipes(branch, sha);
- });
- });
-}
diff --git a/app_dart/test/service/build_status_provider_test.dart b/app_dart/test/service/build_status_provider_test.dart
deleted file mode 100644
index d241e47..0000000
--- a/app_dart/test/service/build_status_provider_test.dart
+++ /dev/null
@@ -1,249 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:cocoon_service/src/model/appengine/commit.dart';
-import 'package:cocoon_service/src/model/appengine/task.dart';
-import 'package:cocoon_service/src/service/build_status_provider.dart';
-import 'package:cocoon_service/src/service/datastore.dart';
-import 'package:gcloud/db.dart';
-import 'package:github/github.dart';
-import 'package:test/test.dart';
-
-import '../src/datastore/fake_config.dart';
-import '../src/datastore/fake_datastore.dart';
-import '../src/utilities/entity_generators.dart';
-
-List<Commit> oneCommit = <Commit>[
- Commit(
- key: Key<String>.emptyKey(Partition('ns')).append(Commit, id: 'sha1'),
- repository: 'flutter/flutter',
- sha: 'sha1',
- ),
-];
-
-List<Commit> twoCommits = <Commit>[
- Commit(
- key: Key<String>.emptyKey(Partition('ns')).append(Commit, id: 'sha1'),
- repository: 'flutter/flutter',
- sha: 'sha1',
- ),
- Commit(
- key: Key<String>.emptyKey(Partition('ns')).append(Commit, id: 'sha2'),
- repository: 'flutter/flutter',
- sha: 'sha2',
- ),
-];
-
-List<Task> allGreen = <Task>[
- generateTask(1, status: Task.statusSucceeded),
- generateTask(2, status: Task.statusSucceeded),
- generateTask(3, status: Task.statusSucceeded),
-];
-
-List<Task> allRed = <Task>[
- generateTask(1, status: Task.statusFailed),
- generateTask(2, status: Task.statusFailed),
- generateTask(3, status: Task.statusFailed),
-];
-
-List<Task> middleTaskFailed = <Task>[
- generateTask(1, status: Task.statusSucceeded),
- generateTask(2, status: Task.statusFailed),
- generateTask(3, status: Task.statusSucceeded),
-];
-
-List<Task> middleTaskCanceled = <Task>[
- generateTask(1, status: Task.statusSucceeded),
- generateTask(2, status: Task.statusCancelled),
- generateTask(3, status: Task.statusSucceeded),
-];
-
-List<Task> middleTaskFlakyFailed = <Task>[
- generateTask(1, status: Task.statusSucceeded),
- generateTask(2, status: Task.statusFailed, isFlaky: true),
- generateTask(3, status: Task.statusSucceeded),
-];
-
-List<Task> middleTaskInProgress = <Task>[
- generateTask(1, status: Task.statusSucceeded),
- generateTask(2, status: Task.statusInProgress),
- generateTask(3, status: Task.statusSucceeded),
-];
-
-List<Task> middleTaskRerunning = <Task>[
- generateTask(1, status: Task.statusSucceeded),
- generateTask(2, status: Task.statusNew, attempts: 2),
- generateTask(3, status: Task.statusSucceeded),
-];
-
-List<Task> middleTaskRerunGreen = <Task>[
- generateTask(1, status: Task.statusSucceeded),
- generateTask(2, status: Task.statusSucceeded, attempts: 2),
- generateTask(3, status: Task.statusSucceeded),
-];
-
-List<Task> middleTaskInfraFailure = <Task>[
- generateTask(1, status: Task.statusSucceeded),
- generateTask(2, status: Task.statusInfraFailure),
- generateTask(3, status: Task.statusSucceeded),
-];
-
-void main() {
- group('BuildStatusProvider', () {
- late FakeDatastoreDB db;
- late BuildStatusService buildStatusService;
- FakeConfig config;
- DatastoreService datastoreService;
-
- final RepositorySlug slug = RepositorySlug('flutter', 'flutter');
-
- setUp(() {
- db = FakeDatastoreDB();
- config = FakeConfig(dbValue: db);
- datastoreService = DatastoreService(config.db, 5);
- buildStatusService = BuildStatusService.defaultProvider(datastoreService);
- });
-
- group('calculateStatus', () {
- test('returns failure if there are no commits', () async {
- final BuildStatus? status = await buildStatusService.calculateCumulativeStatus(slug);
- expect(status, BuildStatus.failure(const <String>[]));
- });
-
- test('returns success if top commit is all green', () async {
- db.addOnQuery<Commit>((Iterable<Commit> results) => oneCommit);
- db.addOnQuery<Task>((Iterable<Task> results) => allGreen);
- final BuildStatus? status = await buildStatusService.calculateCumulativeStatus(slug);
- expect(status, BuildStatus.success());
- });
-
- test('returns success if top commit is all green followed by red commit', () async {
- db.addOnQuery<Commit>((Iterable<Commit> results) => twoCommits);
- int row = 0;
- db.addOnQuery<Task>((Iterable<Task> results) {
- return row++ == 0 ? allGreen : middleTaskFailed;
- });
- final BuildStatus? status = await buildStatusService.calculateCumulativeStatus(slug);
- expect(status, BuildStatus.success());
- });
-
- test('returns failure if last commit contains any red tasks', () async {
- db.addOnQuery<Commit>((Iterable<Commit> results) => oneCommit);
- db.addOnQuery<Task>((Iterable<Task> results) => middleTaskFailed);
- final BuildStatus? status = await buildStatusService.calculateCumulativeStatus(slug);
- expect(status, BuildStatus.failure(const <String>['task2']));
- });
-
- test('returns failure if last commit contains any canceled tasks', () async {
- db.addOnQuery<Commit>((Iterable<Commit> results) => oneCommit);
- db.addOnQuery<Task>((Iterable<Task> results) => middleTaskCanceled);
- final BuildStatus? status = await buildStatusService.calculateCumulativeStatus(slug);
- expect(status, BuildStatus.failure(const <String>['task2']));
- });
-
- test('ensure failed task do not have duplicates when last consecutive commits contains red tasks', () async {
- db.addOnQuery<Commit>((Iterable<Commit> results) => twoCommits);
- db.addOnQuery<Task>((Iterable<Task> results) => middleTaskFailed);
-
- final BuildStatus? status = await buildStatusService.calculateCumulativeStatus(slug);
- expect(status, BuildStatus.failure(const <String>['task2']));
- });
-
- test('ignores failures on flaky commits', () async {
- db.addOnQuery<Commit>((Iterable<Commit> results) => oneCommit);
- db.addOnQuery<Task>((Iterable<Task> results) => middleTaskFlakyFailed);
- final BuildStatus? status = await buildStatusService.calculateCumulativeStatus(slug);
- expect(status, BuildStatus.success());
- });
-
- test('returns success if partial green, and all unfinished tasks were last green', () async {
- db.addOnQuery<Commit>((Iterable<Commit> results) => twoCommits);
- int row = 0;
- db.addOnQuery<Task>((Iterable<Task> results) {
- return row++ == 0 ? middleTaskInProgress : allGreen;
- });
- final BuildStatus? status = await buildStatusService.calculateCumulativeStatus(slug);
- expect(status, BuildStatus.success());
- });
-
- test('returns failure if partial green, and any unfinished task was last red', () async {
- db.addOnQuery<Commit>((Iterable<Commit> results) => twoCommits);
- int row = 0;
- db.addOnQuery<Task>((Iterable<Task> results) {
- return row++ == 0 ? middleTaskInProgress : middleTaskFailed;
- });
- final BuildStatus? status = await buildStatusService.calculateCumulativeStatus(slug);
- expect(status, BuildStatus.failure(const <String>['task2']));
- });
-
- test('returns failure when green but a task is rerunning', () async {
- db.addOnQuery<Commit>((Iterable<Commit> results) => twoCommits);
- int row = 0;
- db.addOnQuery<Task>((Iterable<Task> results) {
- return row++ == 0 ? middleTaskRerunning : allGreen;
- });
- final BuildStatus? status = await buildStatusService.calculateCumulativeStatus(slug);
- expect(status, BuildStatus.failure(const <String>['task2']));
- });
-
- test('returns failure when a task has an infra failure', () async {
- db.addOnQuery<Commit>((Iterable<Commit> results) => twoCommits);
- int row = 0;
- db.addOnQuery<Task>((Iterable<Task> results) {
- return row++ == 0 ? middleTaskInfraFailure : allGreen;
- });
- final BuildStatus? status = await buildStatusService.calculateCumulativeStatus(slug);
- expect(status, BuildStatus.failure(const <String>['task2']));
- });
-
- test('returns success when all green with a successful rerun', () async {
- db.addOnQuery<Commit>((Iterable<Commit> results) => twoCommits);
- int row = 0;
- db.addOnQuery<Task>((Iterable<Task> results) {
- return row++ == 0 ? middleTaskRerunGreen : allRed;
- });
- final BuildStatus? status = await buildStatusService.calculateCumulativeStatus(slug);
- expect(status, BuildStatus.success());
- });
-
- test('return status when with branch parameter', () async {
- final Commit commit1 = generateCommit(1, branch: 'flutter-0.0-candidate.0');
- final Commit commit2 = generateCommit(2, branch: 'master');
- db.values[commit1.key] = commit1;
- db.values[commit2.key] = commit2;
-
- final Task task1 = Task(
- key: commit1.key.append(Task, id: 1),
- commitKey: commit1.key,
- name: 'task1',
- status: Task.statusSucceeded,
- stageName: 'stage1',
- );
-
- db.values[task1.key] = task1;
-
- // Test master branch.
- final List<CommitStatus> statuses1 = await buildStatusService
- .retrieveCommitStatus(
- limit: 5,
- slug: slug,
- )
- .toList();
- expect(statuses1.length, 1);
- expect(statuses1.first.commit.branch, 'master');
-
- // Test dev branch.
- final List<CommitStatus> statuses2 = await buildStatusService
- .retrieveCommitStatus(
- limit: 5,
- branch: 'flutter-0.0-candidate.0',
- slug: slug,
- )
- .toList();
- expect(statuses2.length, 1);
- expect(statuses2.first.commit.branch, 'flutter-0.0-candidate.0');
- });
- });
- });
-}
diff --git a/app_dart/test/service/buildbucket_test.dart b/app_dart/test/service/buildbucket_test.dart
deleted file mode 100644
index 9e3c1de..0000000
--- a/app_dart/test/service/buildbucket_test.dart
+++ /dev/null
@@ -1,377 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-import 'dart:convert';
-import 'dart:io';
-
-import 'package:cocoon_service/src/model/luci/buildbucket.dart';
-import 'package:cocoon_service/src/request_handling/body.dart';
-import 'package:cocoon_service/src/service/buildbucket.dart';
-import 'package:googleapis_auth/auth_io.dart';
-import 'package:http/http.dart' as http;
-import 'package:http/testing.dart';
-import 'package:mockito/mockito.dart';
-import 'package:test/test.dart';
-
-import '../src/utilities/mocks.dart';
-
-void main() {
- group('BatchResponse tests', () {
- test('fromJson returns an empty list', () {
- const String jsonString = '{"responses":[{"searchBuilds":{}},{"searchBuilds":{}}]}';
- final Map<String, dynamic> map = json.decode(jsonString) as Map<String, dynamic>;
- final BatchResponse response = BatchResponse.fromJson(map);
- expect(response, isNotNull);
- expect(response.responses, isNotNull);
- });
- });
-
- group('Client tests', () {
- late MockClient httpClient;
- late MockAccessTokenService mockAccessTokenProvider;
-
- const BuilderId builderId = BuilderId(
- bucket: 'prod',
- builder: 'Linux',
- project: 'flutter',
- );
-
- setUp(() {
- httpClient = MockClient((_) => throw Exception('Client not defined'));
- mockAccessTokenProvider = MockAccessTokenService();
- });
-
- Future<T> httpTest<R extends JsonBody, T>(
- R request,
- String response,
- String urlPrefix,
- String expectedPath,
- Future<T> Function(BuildBucketClient) requestCallback,
- ) async {
- when(mockAccessTokenProvider.createAccessToken()).thenAnswer((_) async {
- return AccessToken('Bearer', 'data', DateTime.utc(2119));
- });
- httpClient = MockClient((http.Request request) async {
- expect(request.headers['content-type'], 'application/json; charset=utf-8');
- expect(request.headers['accept'], 'application/json');
- expect(request.headers['authorization'], 'Bearer data');
- if (request.method == 'POST' && request.url.toString() == 'https://localhost/$urlPrefix/$expectedPath') {
- return http.Response(response, HttpStatus.accepted);
- }
- return http.Response('Test exception: A mock response was not returned', HttpStatus.internalServerError);
- });
- final BuildBucketClient client = BuildBucketClient(
- httpClient: httpClient,
- accessTokenService: mockAccessTokenProvider,
- );
- final T result = await requestCallback(client);
- return result;
- }
-
- test('Throws the right exception', () async {
- when(mockAccessTokenProvider.createAccessToken()).thenAnswer((_) async {
- return AccessToken('Bearer', 'data', DateTime.utc(2119));
- });
- httpClient = MockClient((_) async => http.Response('Error', HttpStatus.forbidden));
- final BuildBucketClient client = BuildBucketClient(
- buildBucketBuildUri: 'https://localhost',
- httpClient: httpClient,
- accessTokenService: mockAccessTokenProvider,
- );
- try {
- await client.batch(const BatchRequest());
- } on BuildBucketException catch (ex) {
- expect(ex.statusCode, HttpStatus.forbidden);
- expect(ex.message, 'Error');
- return;
- }
- fail('Did not throw expected exception');
- });
-
- test('ScheduleBuild', () async {
- const ScheduleBuildRequest request = ScheduleBuildRequest(
- builderId: builderId,
- experimental: Trinary.yes,
- tags: <String, List<String>>{
- 'user_agent': <String>['flutter_cocoon'],
- 'flutter_pr': <String>['true', '1'],
- 'cipd_version': <String>['/refs/heads/main'],
- },
- properties: <String, String>{
- 'git_url': 'https://github.com/flutter/flutter',
- 'git_ref': 'refs/pull/1/head',
- },
- );
-
- final Build build = await httpTest<ScheduleBuildRequest, Build>(
- request,
- buildJson,
- 'builds',
- 'ScheduleBuild',
- (BuildBucketClient client) => client.scheduleBuild(request, buildBucketUri: 'https://localhost/builds'),
- );
-
- expect(build.id, '123');
- expect(build.tags!.length, 3);
- expect(build.tags, <String?, List<String?>>{
- 'user_agent': <String>['flutter_cocoon'],
- 'flutter_pr': <String>['1'],
- 'cipd_version': <String>['/refs/heads/main'],
- });
- });
-
- test('CancelBuild', () async {
- const CancelBuildRequest request = CancelBuildRequest(
- id: '1234',
- summaryMarkdown: 'Because I felt like it.',
- );
-
- final Build build = await httpTest<CancelBuildRequest, Build>(
- request,
- buildJson,
- 'builds',
- 'CancelBuild',
- (BuildBucketClient client) => client.cancelBuild(request, buildBucketUri: 'https://localhost/builds'),
- );
-
- expect(build.id, '123');
- expect(build.tags!.length, 3);
- });
-
- test('BatchBuildRequest', () async {
- const BatchRequest request = BatchRequest(
- requests: <Request>[
- Request(
- scheduleBuild: ScheduleBuildRequest(
- builderId: builderId,
- experimental: Trinary.yes,
- tags: <String, List<String>>{
- 'user_agent': <String>['flutter_cocoon'],
- 'flutter_pr': <String>['true', '1'],
- 'cipd_version': <String>['/refs/heads/main'],
- },
- properties: <String, String>{
- 'git_url': 'https://github.com/flutter/flutter',
- 'git_ref': 'refs/pull/1/head',
- },
- ),
- ),
- ],
- );
-
- final BatchResponse response = await httpTest<BatchRequest, BatchResponse>(
- request,
- batchJson,
- 'builds',
- 'Batch',
- (BuildBucketClient client) => client.batch(request, buildBucketUri: 'https://localhost/builds'),
- );
- expect(response.responses!.length, 1);
- expect(response.responses!.first.getBuild!.status, Status.success);
- expect(response.responses!.first.getBuild!.tags, <String?, List<String?>>{
- 'user_agent': <String>['flutter_cocoon'],
- 'flutter_pr': <String>['1'],
- 'cipd_version': <String>['/refs/heads/main'],
- });
- });
-
- test('Batch', () async {
- const BatchRequest request = BatchRequest(
- requests: <Request>[
- Request(
- getBuild: GetBuildRequest(
- builderId: builderId,
- buildNumber: 123,
- ),
- ),
- ],
- );
-
- final BatchResponse response = await httpTest<BatchRequest, BatchResponse>(
- request,
- batchJson,
- 'builds',
- 'Batch',
- (BuildBucketClient client) => client.batch(request, buildBucketUri: 'https://localhost/builds'),
- );
-
- expect(response.responses!.length, 1);
- expect(response.responses!.first.getBuild!.status, Status.success);
- });
-
- test('GetBuild', () async {
- const GetBuildRequest request = GetBuildRequest(
- id: '1234',
- );
-
- final Build build = await httpTest<GetBuildRequest, Build>(
- request,
- buildJson,
- 'builds',
- 'GetBuild',
- (BuildBucketClient client) => client.getBuild(request, buildBucketUri: 'https://localhost/builds'),
- );
-
- expect(build.id, '123');
- expect(build.tags!.length, 3);
- });
-
- test('SearchBuilds', () async {
- const SearchBuildsRequest request = SearchBuildsRequest(
- predicate: BuildPredicate(
- tags: <String, List<String>>{
- 'flutter_pr': <String>['1'],
- },
- ),
- );
-
- final SearchBuildsResponse response = await httpTest<SearchBuildsRequest, SearchBuildsResponse>(
- request,
- searchJson,
- 'builds',
- 'SearchBuilds',
- (BuildBucketClient client) => client.searchBuilds(request, buildBucketUri: 'https://localhost/builds'),
- );
-
- expect(response.builds!.length, 1);
- expect(response.builds!.first.number, 9151);
- });
-
- test('ListBuilders', () async {
- const ListBuildersRequest request = ListBuildersRequest(project: 'test');
-
- final ListBuildersResponse listBuildersResponse = await httpTest<ListBuildersRequest, ListBuildersResponse>(
- request,
- builderJson,
- 'builders',
- 'ListBuilders',
- (BuildBucketClient client) => client.listBuilders(request, buildBucketUri: 'https://localhost/builders'),
- );
-
- expect(listBuildersResponse.builders!.length, 2);
- expect(listBuildersResponse.builders!.map((e) => e.id!.builder!).toList(), <String>['Linux test', 'Mac test']);
- });
- });
-}
-
-const String builderJson = '''${BuildBucketClient.kRpcResponseGarbage}
-{
- "builders": [{
- "id": {
- "project": "flutter",
- "bucket": "prod",
- "builder": "Linux test"
- }
- }, {
- "id": {
- "project": "flutter",
- "bucket": "prod",
- "builder": "Mac test"
- }
- }]
-}''';
-
-const String searchJson = '''${BuildBucketClient.kRpcResponseGarbage}
-{
- "builds": [
- {
- "status": "SUCCESS",
- "updateTime": "2019-07-26T20:43:52.875240Z",
- "createdBy": "project:flutter",
- "builder": {
- "project": "flutter",
- "builder": "Linux",
- "bucket": "prod"
- },
- "number": 9151,
- "id": "8906840690092270320",
- "startTime": "2019-07-26T20:10:22.271996Z",
- "input": {
- "gitilesCommit": {
- "project": "external/github.com/flutter/flutter",
- "host": "chromium.googlesource.com",
- "ref": "refs/heads/master",
- "id": "3068fc4f7c78599ab4a09b096f0672e8510fc7e6"
- }
- },
- "endTime": "2019-07-26T20:43:52.341494Z",
- "createTime": "2019-07-26T20:10:15.744632Z"
- }
- ]
-}''';
-const String batchJson = '''${BuildBucketClient.kRpcResponseGarbage}
-{
- "responses": [
- {
- "getBuild": {
- "status": "SUCCESS",
- "updateTime": "2019-07-15T23:20:56.930928Z",
- "createdBy": "project:flutter",
- "builder": {
- "project": "flutter",
- "builder": "Linux",
- "bucket": "prod"
- },
- "number": 9000,
- "id": "8907827286280251904",
- "startTime": "2019-07-15T22:49:06.222424Z",
- "input": {
- "gitilesCommit": {
- "project": "external/github.com/flutter/flutter",
- "host": "chromium.googlesource.com",
- "ref": "refs/heads/master",
- "id": "6b17840cbf1a7d7b6208117226ded21a5ec0d55c"
- }
- },
- "endTime": "2019-07-15T23:20:32.610402Z",
- "createTime": "2019-07-15T22:48:44.299749Z",
- "tags": [
- {
- "key": "user_agent",
- "value": "flutter_cocoon"
- },
- {
- "key": "flutter_pr",
- "value": "1"
- },
- {
- "key": "cipd_version",
- "value": "/refs/heads/main"
- }
- ]
- }
- }
- ]
-}''';
-
-const String buildJson = '''${BuildBucketClient.kRpcResponseGarbage}
-{
- "id": "123",
- "builder": {
- "project": "flutter",
- "bucket": "prod",
- "builder": "Linux"
- },
- "number": 321,
- "createdBy": "cocoon@cocoon",
- "canceledBy": null,
- "startTime": "2019-08-01T11:00:00",
- "endTime": null,
- "status": "SCHEDULED",
- "input": {
- "experimental": true
- },
- "tags": [{
- "key": "user_agent",
- "value": "flutter_cocoon"
- }, {
- "key": "flutter_pr",
- "value": "1"
- },
- {
- "key": "cipd_version",
- "value": "/refs/heads/main"
- }]
-}''';
diff --git a/app_dart/test/service/cache_service_test.dart b/app_dart/test/service/cache_service_test.dart
deleted file mode 100644
index d2d3310..0000000
--- a/app_dart/test/service/cache_service_test.dart
+++ /dev/null
@@ -1,269 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-import 'dart:typed_data';
-
-import 'package:cocoon_service/src/service/cache_service.dart';
-import 'package:mockito/mockito.dart';
-import 'package:neat_cache/neat_cache.dart';
-import 'package:test/test.dart';
-
-import '../src/utilities/mocks.dart';
-
-void main() {
- group('CacheService', () {
- late CacheService cache;
-
- const String testSubcacheName = 'test';
-
- setUp(() {
- cache = CacheService(inMemory: true, inMemoryMaxNumberEntries: 1);
- });
-
- test('returns null when no value exists', () async {
- final Uint8List? value = await cache.getOrCreate(
- testSubcacheName,
- 'abc',
- createFn: null,
- );
-
- expect(value, isNull);
- });
-
- test('returns value when it exists', () async {
- const String testKey = 'abc';
- final Uint8List expectedValue = Uint8List.fromList('123'.codeUnits);
-
- await cache.set(testSubcacheName, testKey, expectedValue);
-
- final Uint8List? value = await cache.getOrCreate(
- testSubcacheName,
- testKey,
- createFn: null,
- );
-
- expect(value, expectedValue);
- });
-
- test('last used value is rotated out of cache if cache is full', () async {
- const String testKey1 = 'abc';
- const String testKey2 = 'def';
- final Uint8List expectedValue1 = Uint8List.fromList('123'.codeUnits);
- final Uint8List expectedValue2 = Uint8List.fromList('456'.codeUnits);
-
- await cache.set(testSubcacheName, testKey1, expectedValue1);
- await cache.set(testSubcacheName, testKey2, expectedValue2);
-
- final Uint8List? value1 = await cache.getOrCreate(
- testSubcacheName,
- testKey1,
- createFn: null,
- );
- expect(value1, null);
-
- final Uint8List? value2 = await cache.getOrCreate(
- testSubcacheName,
- testKey2,
- createFn: null,
- );
- expect(value2, expectedValue2);
- });
-
- test('retries when get throws exception', () async {
- final Cache<Uint8List> mockMainCache = MockCache();
- final Cache<Uint8List> mockTestSubcache = MockCache();
- when<Cache<Uint8List>>(mockMainCache.withPrefix(testSubcacheName)).thenReturn(mockTestSubcache);
-
- int getCallCount = 0;
- final Entry<Uint8List> entry = FakeEntry();
- // Only on the first call do we want it to throw the exception.
- when(mockTestSubcache['does not matter']).thenAnswer(
- (Invocation invocation) => getCallCount++ < 1 ? throw Exception('simulate stream sink error') : entry,
- );
-
- cache.cacheValue = mockMainCache;
-
- final Uint8List? value = await cache.getOrCreate(
- testSubcacheName,
- 'does not matter',
- createFn: null,
- );
- verify(mockTestSubcache['does not matter']).called(2);
- expect(value, Uint8List.fromList('abc123'.codeUnits));
- });
-
- test('returns null if reaches max attempts of retries', () async {
- final Cache<Uint8List> mockMainCache = MockCache();
- final Cache<Uint8List> mockTestSubcache = MockCache();
- when<Cache<Uint8List>>(mockMainCache.withPrefix(testSubcacheName)).thenReturn(mockTestSubcache);
-
- int getCallCount = 0;
- final Entry<Uint8List> entry = FakeEntry();
- // Always throw exception until max retries
- when(mockTestSubcache['does not matter']).thenAnswer(
- (Invocation invocation) =>
- getCallCount++ < CacheService.maxCacheGetAttempts ? throw Exception('simulate stream sink error') : entry,
- );
-
- cache.cacheValue = mockMainCache;
-
- final Uint8List? value = await cache.getOrCreate(
- testSubcacheName,
- 'does not matter',
- createFn: null,
- );
- verify(mockTestSubcache['does not matter']).called(CacheService.maxCacheGetAttempts);
- expect(value, isNull);
- });
-
- test('creates value if given createFn', () async {
- final Uint8List cat = Uint8List.fromList('cat'.codeUnits);
- Future<Uint8List> createCat() async => cat;
-
- final Uint8List? value = await cache.getOrCreate(testSubcacheName, 'dog', createFn: createCat);
-
- expect(value, cat);
- });
-
- test('purge removes value', () async {
- const String testKey = 'abc';
- final Uint8List expectedValue = Uint8List.fromList('123'.codeUnits);
-
- await cache.set(testSubcacheName, testKey, expectedValue);
-
- final Uint8List? value = await cache.getOrCreate(
- testSubcacheName,
- testKey,
- createFn: null,
- );
-
- expect(value, expectedValue);
-
- await cache.purge(testSubcacheName, testKey);
-
- final Uint8List? valueAfterPurge = await cache.getOrCreate(
- testSubcacheName,
- testKey,
- createFn: null,
- );
- expect(valueAfterPurge, isNull);
- });
-
- test('sets ttl from set', () async {
- final Cache<Uint8List> mockMainCache = MockCache();
- final Cache<Uint8List> mockTestSubcache = MockCache();
- when<Cache<Uint8List>>(mockMainCache.withPrefix(testSubcacheName)).thenReturn(mockTestSubcache);
-
- final Entry<Uint8List> entry = MockFakeEntry();
- when(mockTestSubcache['fish']).thenAnswer((Invocation invocation) => entry);
- cache.cacheValue = mockMainCache;
-
- const Duration testDuration = Duration(days: 40);
- when(entry.set(any, testDuration)).thenAnswer((_) async => null);
- verifyNever(entry.set(any, testDuration));
- await cache.set(testSubcacheName, 'fish', Uint8List.fromList('bigger fish'.codeUnits), ttl: testDuration);
- verify(entry.set(any, testDuration)).called(1);
- });
-
- test('sets ttl is passed through correctly from createFn', () async {
- const String value = 'bigger fish';
- final Uint8List valueBytes = Uint8List.fromList(value.codeUnits);
- const Duration testDuration = Duration(days: 40);
-
- final Entry<Uint8List> entry = MockFakeEntry();
- when(entry.set(valueBytes, testDuration)).thenAnswer((_) async => valueBytes);
-
- final Cache<Uint8List> mockTestSubcache = MockCache();
- final Cache<Uint8List> mockMainCache = MockCache();
- when<Cache<Uint8List>>(mockMainCache.withPrefix(testSubcacheName)).thenReturn(mockTestSubcache);
- when(mockTestSubcache['fish']).thenAnswer((Invocation invocation) => entry);
- cache.cacheValue = mockMainCache;
-
- verifyNever(entry.set(any, testDuration));
- await cache.getOrCreate(
- testSubcacheName,
- 'fish',
- createFn: () async => valueBytes,
- ttl: testDuration,
- );
- verify(entry.set(any, testDuration)).called(1);
- });
-
- test('set does not block read attempt', () async {
- const String testKey = 'abc';
- final Uint8List expectedValue = Uint8List.fromList('123'.codeUnits);
-
- final cacheWrite = cache.setWithLocking(testSubcacheName, testKey, expectedValue);
- Uint8List? valueAfterSet = await cache.getOrCreateWithLocking(
- testSubcacheName,
- testKey,
- createFn: null,
- );
-
- expect(valueAfterSet, null);
- await cacheWrite;
- valueAfterSet = await cache.getOrCreateWithLocking(
- testSubcacheName,
- testKey,
- createFn: null,
- );
- expect(valueAfterSet, expectedValue);
- });
-
- test('read locks are not blocking', () async {
- const String testKey = 'abc';
- final Uint8List expectedValue = Uint8List.fromList('123'.codeUnits);
-
- await cache.setWithLocking(testSubcacheName, testKey, expectedValue);
- final Future<Uint8List?> valueAfterSet = cache.getOrCreateWithLocking(
- testSubcacheName,
- testKey,
- createFn: null,
- );
- final Uint8List? valueAfterSet2 = await cache.getOrCreateWithLocking(
- testSubcacheName,
- testKey,
- createFn: null,
- );
-
- expect(valueAfterSet2, expectedValue);
- await valueAfterSet.then((value) => expect(value, expectedValue));
- });
-
- test('write locks are blocking', () async {
- const String testKey = 'abc';
- final Uint8List expectedValue = Uint8List.fromList('123'.codeUnits);
- final Uint8List newValue = Uint8List.fromList('345'.codeUnits);
-
- final cacheWrite = cache.setWithLocking(testSubcacheName, testKey, expectedValue);
- final cacheWrite2 = cache.setWithLocking(testSubcacheName, testKey, newValue);
- await cacheWrite;
- final Uint8List? readValue = await cache.getOrCreateWithLocking(
- testSubcacheName,
- testKey,
- createFn: null,
- );
- expect(readValue, expectedValue);
- await cacheWrite2;
- });
- });
-}
-
-class FakeEntry extends Entry<Uint8List> {
- Uint8List value = Uint8List.fromList('abc123'.codeUnits);
-
- @override
- Future<Uint8List> get([Future<Uint8List?> Function()? create, Duration? ttl]) async => value;
-
- @override
- Future<void> purge({int retries = 0}) => throw UnimplementedError();
-
- @override
- Future<Uint8List?> set(Uint8List? value, [Duration? ttl]) async {
- value = value;
-
- return value;
- }
-}
diff --git a/app_dart/test/service/commit_service_test.dart b/app_dart/test/service/commit_service_test.dart
deleted file mode 100644
index 5a88ac6..0000000
--- a/app_dart/test/service/commit_service_test.dart
+++ /dev/null
@@ -1,184 +0,0 @@
-// Copyright 2021 The Flutter 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 'package:cocoon_service/src/model/appengine/commit.dart';
-import 'package:cocoon_service/src/service/commit_service.dart';
-import 'package:github/github.dart';
-
-import 'package:mockito/mockito.dart';
-import 'package:test/test.dart';
-import 'package:github/hooks.dart';
-
-import '../src/datastore/fake_datastore.dart';
-import '../src/utilities/entity_generators.dart';
-import '../src/utilities/mocks.mocks.dart';
-import '../src/utilities/webhook_generators.dart';
-
-void main() {
- late MockConfig config;
- late FakeDatastoreDB db;
- late CommitService commitService;
- late MockGithubService githubService;
- late MockRepositoriesService repositories;
- late MockGitHub github;
- const String owner = 'flutter';
- const String repository = 'flutter';
- const String branch = 'coolest-branch';
- const String sha = '1234';
- const String message = 'Adding null safety';
- const String avatarUrl = 'https://avatars.githubusercontent.com/u/fake-user-num';
- const String username = 'AwesomeGithubUser';
- const String dateTimeAsString = '2023-08-18T19:27:00Z';
-
- setUp(() {
- db = FakeDatastoreDB();
- github = MockGitHub();
- githubService = MockGithubService();
- when(githubService.github).thenReturn(github);
- repositories = MockRepositoriesService();
- when(github.repositories).thenReturn(repositories);
- config = MockConfig();
- commitService = CommitService(config: config);
-
- when(config.createDefaultGitHubService()).thenAnswer((_) async => githubService);
- when(config.db).thenReturn(db);
- });
-
- group('handleCreateGithubRequest', () {
- test('adds commit to db if it does not exist in the datastore', () async {
- expect(db.values.values.whereType<Commit>().length, 0);
-
- when(githubService.getReference(RepositorySlug(owner, repository), 'heads/$branch'))
- .thenAnswer((Invocation invocation) {
- return Future<GitReference>.value(
- GitReference(ref: 'refs/$branch', object: GitObject('', sha, '')),
- );
- });
-
- when(repositories.getCommit(RepositorySlug(owner, repository), sha)).thenAnswer((Invocation invocation) {
- return Future<RepositoryCommit>.value(
- RepositoryCommit(
- sha: sha,
- author: User(
- login: username,
- avatarUrl: avatarUrl,
- ),
- commit: GitCommit(message: message),
- ),
- );
- });
-
- final CreateEvent createEvent = generateCreateBranchEvent(branch, '$owner/$repository');
- await commitService.handleCreateGithubRequest(createEvent);
-
- expect(db.values.values.whereType<Commit>().length, 1);
- final Commit commit = db.values.values.whereType<Commit>().single;
- expect(commit.repository, '$owner/$repository');
- expect(commit.message, message);
- expect(commit.key.id, '$owner/$repository/$branch/$sha');
- expect(commit.sha, sha);
- expect(commit.author, username);
- expect(commit.authorAvatarUrl, avatarUrl);
- expect(commit.branch, branch);
- });
-
- test('does not add commit to db if it exists in the datastore', () async {
- final Commit existingCommit = generateCommit(
- 1,
- sha: sha,
- branch: branch,
- owner: owner,
- repo: repository,
- timestamp: 0,
- );
- final List<Commit> datastoreCommit = <Commit>[existingCommit];
- await config.db.commit(inserts: datastoreCommit);
- expect(db.values.values.whereType<Commit>().length, 1);
-
- when(githubService.getReference(RepositorySlug(owner, repository), 'heads/$branch'))
- .thenAnswer((Invocation invocation) {
- return Future<GitReference>.value(
- GitReference(ref: 'refs/$branch', object: GitObject('', sha, '')),
- );
- });
-
- when(repositories.getCommit(RepositorySlug(owner, repository), sha)).thenAnswer((Invocation invocation) {
- return Future<RepositoryCommit>.value(
- RepositoryCommit(
- sha: sha,
- author: User(
- createdAt: DateTime.parse(dateTimeAsString),
- login: username,
- avatarUrl: avatarUrl,
- ),
- commit: GitCommit(message: message),
- ),
- );
- });
-
- final CreateEvent createEvent = generateCreateBranchEvent(branch, '$owner/$repository');
- await commitService.handleCreateGithubRequest(createEvent);
-
- expect(db.values.values.whereType<Commit>().length, 1);
- final Commit commit = db.values.values.whereType<Commit>().single;
- expect(commit, existingCommit);
- });
- });
-
- group('handlePushGithubRequest', () {
- test('adds commit to db if it does not exist in the datastore', () async {
- expect(db.values.values.whereType<Commit>().length, 0);
-
- final Map<String, dynamic> pushEvent = generatePushEvent(
- branch,
- owner,
- repository,
- message: message,
- sha: sha,
- avatarUrl: avatarUrl,
- username: username,
- );
- await commitService.handlePushGithubRequest(pushEvent);
-
- expect(db.values.values.whereType<Commit>().length, 1);
- final Commit commit = db.values.values.whereType<Commit>().single;
- expect(commit.repository, '$owner/$repository');
- expect(commit.message, message);
- expect(commit.key.id, '$owner/$repository/$branch/$sha');
- expect(commit.sha, sha);
- expect(commit.author, username);
- expect(commit.authorAvatarUrl, avatarUrl);
- expect(commit.branch, branch);
- });
-
- test('does not add commit to db if it exists in the datastore', () async {
- final Commit existingCommit = generateCommit(
- 1,
- sha: sha,
- branch: branch,
- owner: owner,
- repo: repository,
- timestamp: 0,
- );
- final List<Commit> datastoreCommit = <Commit>[existingCommit];
- await config.db.commit(inserts: datastoreCommit);
- expect(db.values.values.whereType<Commit>().length, 1);
-
- final Map<String, dynamic> pushEvent = generatePushEvent(
- branch,
- owner,
- repository,
- message: message,
- sha: sha,
- avatarUrl: avatarUrl,
- username: username,
- );
- await commitService.handlePushGithubRequest(pushEvent);
-
- expect(db.values.values.whereType<Commit>().length, 1);
- final Commit commit = db.values.values.whereType<Commit>().single;
- expect(commit, existingCommit);
- });
- });
-}
diff --git a/app_dart/test/service/config_test.dart b/app_dart/test/service/config_test.dart
deleted file mode 100644
index c653139..0000000
--- a/app_dart/test/service/config_test.dart
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2020 The Flutter 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:typed_data';
-
-import 'package:cocoon_service/cocoon_service.dart';
-import 'package:github/github.dart';
-import 'package:test/test.dart';
-
-import '../src/datastore/fake_datastore.dart';
-
-void main() {
- group('Config', () {
- FakeDatastoreDB datastore;
- late CacheService cacheService;
- late Config config;
- setUp(() {
- datastore = FakeDatastoreDB();
- cacheService = CacheService(inMemory: true);
- config = Config(datastore, cacheService);
- });
- test('githubAppInstallations when builder config does not exist', () async {
- const String configValue = '{"godofredoc/cocoon":{"installation_id":"123"}}';
- final Uint8List cachedValue = Uint8List.fromList(configValue.codeUnits);
-
- await cacheService.set(
- Config.configCacheName,
- 'githubapp_installations',
- cachedValue,
- );
- final Map<String, dynamic> installation = await config.githubAppInstallations;
- expect(installation['godofredoc/cocoon']['installation_id'], equals('123'));
- });
-
- test('generateGithubToken pulls from cache', () async {
- const String configValue = 'githubToken';
- final Uint8List cachedValue = Uint8List.fromList(configValue.codeUnits);
- await cacheService.set(
- Config.configCacheName,
- 'githubToken-${Config.flutterSlug}',
- cachedValue,
- );
-
- final String githubToken = await config.generateGithubToken(Config.flutterSlug);
- expect(githubToken, 'githubToken');
- });
-
- test('Returns the right flutter gold alert', () {
- expect(
- config.flutterGoldAlertConstant(RepositorySlug.full('flutter/flutter')),
- contains('package:flutter'),
- );
- expect(
- config.flutterGoldAlertConstant(RepositorySlug.full('flutter/engine')),
- isNot(contains('package:flutter')),
- );
- });
- });
-}
diff --git a/app_dart/test/service/datastore_test.dart b/app_dart/test/service/datastore_test.dart
deleted file mode 100644
index 97de03d..0000000
--- a/app_dart/test/service/datastore_test.dart
+++ /dev/null
@@ -1,247 +0,0 @@
-// Copyright 2020 The Flutter 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 'package:cocoon_service/src/model/appengine/commit.dart';
-import 'package:cocoon_service/src/model/appengine/task.dart';
-import 'package:cocoon_service/src/service/config.dart';
-import 'package:cocoon_service/src/service/datastore.dart';
-import 'package:gcloud/datastore.dart' as gcloud_datastore;
-import 'package:gcloud/db.dart';
-import 'package:grpc/grpc.dart';
-import 'package:retry/retry.dart';
-import 'package:test/test.dart';
-
-import '../src/datastore/fake_config.dart';
-import '../src/datastore/fake_datastore.dart';
-import '../src/utilities/entity_generators.dart';
-
-class Counter {
- int count = 0;
- void increase() {
- count = count + 1;
- }
-
- int value() {
- return count;
- }
-}
-
-void main() {
- group('Datastore', () {
- late FakeConfig config;
- late FakeDatastoreDB db;
- late DatastoreService datastoreService;
- late Commit commit;
-
- setUp(() {
- db = FakeDatastoreDB();
- config = FakeConfig(dbValue: db);
- datastoreService = DatastoreService(config.db, 5);
- commit = Commit(
- key: config.db.emptyKey.append(Commit, id: 'abc_master'),
- sha: 'abc_master',
- repository: Config.flutterSlug.fullName,
- branch: 'master',
- );
- });
-
- group('DatasourceService', () {
- setUp(() {});
-
- test('defaultProvider returns a DatasourceService object', () async {
- expect(DatastoreService.defaultProvider(config.db), isA<DatastoreService>());
- });
-
- test('queryRecentCommits', () async {
- for (String branch in <String>['master', 'release']) {
- final Commit commit = Commit(
- key: config.db.emptyKey.append(Commit, id: 'abc_$branch'),
- repository: Config.flutterSlug.fullName,
- sha: 'abc_$branch',
- branch: branch,
- );
- config.db.values[commit.key] = commit;
- }
- // Defaults to master
- List<Commit> commits = await datastoreService.queryRecentCommits(slug: Config.flutterSlug).toList();
- expect(commits, hasLength(1));
- expect(commits[0].branch, equals('master'));
- // Explicit branch
- commits = await datastoreService.queryRecentCommits(branch: 'release', slug: Config.flutterSlug).toList();
- expect(commits, hasLength(1));
- expect(commits[0].branch, equals('release'));
- });
-
- test('queryRecentCommits with slug', () async {
- for (String repo in <String>['flutter', 'engine']) {
- final Commit commit = Commit(
- key: config.db.emptyKey.append(Commit, id: 'flutter/$repo/main/abc'),
- repository: 'flutter/$repo',
- sha: 'abc',
- branch: 'main',
- );
- config.db.values[commit.key] = commit;
- }
- // Only retrieves flutter/flutter
- List<Commit> commits =
- await datastoreService.queryRecentCommits(slug: Config.flutterSlug, branch: 'main').toList();
- expect(commits, hasLength(1));
- expect(commits.single.repository, equals('flutter/flutter'));
- // Only retrieves flutter/engine
- commits = await datastoreService.queryRecentCommits(branch: 'main', slug: Config.engineSlug).toList();
- expect(commits, hasLength(1));
- expect(commits.single.repository, equals('flutter/engine'));
- });
- });
-
- test('queryRecentCommits with repo default branch', () async {
- for (String branch in <String>['master', 'main']) {
- final Commit commit = Commit(
- key: config.db.emptyKey.append(Commit, id: 'abc_$branch'),
- repository: Config.engineSlug.fullName,
- sha: 'abc_$branch',
- branch: branch,
- );
- config.db.values[commit.key] = commit;
- }
- // Pull from main, not master
- final List<Commit> commits = await datastoreService.queryRecentCommits(slug: Config.engineSlug).toList();
- expect(commits, hasLength(1));
- expect(commits[0].branch, equals('main'));
- });
-
- test('queryRecentTasks returns all tasks', () async {
- const String branch = 'main';
- final Commit commit = Commit(
- key: config.db.emptyKey.append(Commit, id: 'abc_$branch'),
- repository: Config.engineSlug.fullName,
- sha: 'abc_$branch',
- branch: branch,
- );
- const int taskNumber = 2;
- for (int i = 0; i < taskNumber; i++) {
- final Task task = generateTask(
- i,
- parent: commit,
- );
- config.db.values[task.key] = task;
- }
-
- config.db.values[commit.key] = commit;
- final List<FullTask> datastoreTasks = await datastoreService
- .queryRecentTasks(
- slug: Config.engineSlug,
- )
- .toList();
- expect(datastoreTasks, hasLength(taskNumber));
- });
-
- test('Shard', () async {
- // default maxEntityGroups = 5
- List<List<Model<dynamic>>> shards = await datastoreService.shard(generateCommits(6));
- expect(shards, hasLength(2));
- expect(shards[0], hasLength(5));
- expect(shards[1], hasLength(1));
- // maxEntitygroups = 2
- datastoreService = DatastoreService(config.db, 2);
- shards = await datastoreService.shard(generateCommits(3));
- expect(shards, hasLength(2));
- expect(shards[0], hasLength(2));
- expect(shards[1], hasLength(1));
- // maxEntityGroups = 1
- datastoreService = DatastoreService(config.db, 1);
- shards = await datastoreService.shard(generateCommits(3));
- expect(shards, hasLength(3));
- expect(shards[0], hasLength(1));
- expect(shards[1], hasLength(1));
- expect(shards[2], hasLength(1));
- });
-
- test('Insert', () async {
- await datastoreService.insert(<Commit>[commit]);
- expect(config.db.values[commit.key], equals(commit));
- });
-
- test('LookupByKey', () async {
- config.db.values[commit.key] = commit;
- final List<Commit?> commits = await datastoreService.lookupByKey(<Key<dynamic>>[commit.key]);
- expect(commits, hasLength(1));
- expect(commits[0], equals(commit));
- });
-
- test('LookupByValue', () async {
- config.db.values[commit.key] = commit;
- final Commit expected = await datastoreService.lookupByValue(commit.key);
- expect(expected, equals(commit));
- });
-
- test('WithTransaction', () async {
- final String? expected = await datastoreService.withTransaction((Transaction transaction) async {
- transaction.queueMutations(inserts: <Commit>[commit]);
- await transaction.commit();
- return 'success';
- });
- expect(expected, equals('success'));
- expect(config.db.values[commit.key], equals(commit));
- });
- });
-
- group('RunTransactionWithRetry', () {
- late RetryOptions retryOptions;
-
- setUp(() {
- retryOptions = const RetryOptions(
- delayFactor: Duration(milliseconds: 1),
- maxDelay: Duration(milliseconds: 2),
- maxAttempts: 2,
- );
- });
-
- test('retriesOnGrpcError', () async {
- final Counter counter = Counter();
- try {
- await runTransactionWithRetries(
- () async {
- counter.increase();
- throw const GrpcError.aborted();
- },
- retryOptions: retryOptions,
- );
- } catch (e) {
- expect(e, isA<GrpcError>());
- }
- expect(counter.value(), greaterThan(1));
- });
- test('retriesTransactionAbortedError', () async {
- final Counter counter = Counter();
- try {
- await runTransactionWithRetries(
- () async {
- counter.increase();
- throw gcloud_datastore.TransactionAbortedError();
- },
- retryOptions: retryOptions,
- );
- } catch (e) {
- expect(e, isA<gcloud_datastore.TransactionAbortedError>());
- }
- expect(counter.value(), greaterThan(1));
- });
- test('DoesNotRetryOnSuccess', () async {
- final Counter counter = Counter();
- await runTransactionWithRetries(
- () async {
- counter.increase();
- },
- retryOptions: retryOptions,
- );
- expect(counter.value(), equals(1));
- });
- });
-}
-
-/// Helper function to generate fake commits
-List<Commit> generateCommits(int i) {
- return List<Commit>.generate(i, (int i) => Commit(repository: 'flutter/flutter', sha: '$i'));
-}
diff --git a/app_dart/test/service/gerrit_service_test.dart b/app_dart/test/service/gerrit_service_test.dart
deleted file mode 100644
index 29c4e19..0000000
--- a/app_dart/test/service/gerrit_service_test.dart
+++ /dev/null
@@ -1,257 +0,0 @@
-// Copyright 2020 The Flutter 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:cocoon_service/src/model/gerrit/commit.dart';
-import 'package:cocoon_service/src/request_handling/exceptions.dart';
-import 'package:cocoon_service/src/service/branch_service.dart';
-import 'package:cocoon_service/src/service/config.dart';
-import 'package:cocoon_service/src/service/gerrit_service.dart';
-import 'package:github/github.dart';
-import 'package:http/http.dart' as http;
-import 'package:http/http.dart';
-import 'package:http/testing.dart';
-import 'package:test/test.dart';
-
-import '../src/datastore/fake_config.dart';
-import '../src/service/fake_auth_client.dart';
-import '../src/utilities/matchers.dart';
-
-void main() {
- late MockClient mockHttpClient;
- late GerritService gerritService;
- group('getBranches', () {
- test('Too many retries raise an exception', () async {
- mockHttpClient = MockClient((_) async => http.Response(')]}\'\n[]', HttpStatus.forbidden));
- gerritService = GerritService(config: FakeConfig(), httpClient: mockHttpClient);
- try {
- await gerritService.branches(
- 'myhost',
- 'a/b/c',
- filterRegex: 'flutter-*',
- );
- } catch (e) {
- expect(e, isA<RetryException>());
- }
- });
- test('Returns a list of branches', () async {
- Request? requestAux;
- const String body =
- ')]}\'\n[{"web_links":[{"name":"browse","url":"https://a.com/branch_a","target":"_blank"}],"ref":"refs/heads/branch_a","revision":"0bc"}]';
- mockHttpClient = MockClient((Request request) async {
- requestAux = request;
- return http.Response(body, HttpStatus.ok);
- });
- gerritService = GerritService(config: FakeConfig(), httpClient: mockHttpClient);
- final List<String> branches = await gerritService.branches(
- 'myhost',
- 'a/b/c',
- filterRegex: 'flutter-*|fuchsia*',
- );
- expect(branches, equals(<String>['refs/heads/branch_a']));
- expect(requestAux!.url.queryParameters, equals(<dynamic, dynamic>{'r': 'flutter-*|fuchsia*'}));
- });
-
- test('No results return an empty list', () async {
- mockHttpClient = MockClient((_) async => http.Response(')]}\'\n[]', HttpStatus.ok));
- gerritService = GerritService(config: FakeConfig(), httpClient: mockHttpClient);
- final List<String> branches = await gerritService.branches(
- 'myhost',
- 'a/b/c',
- filterRegex: 'flutter-',
- );
- expect(branches, equals(<String>[]));
- });
- });
-
- group('commits', () {
- test('Returns a list of commits', () async {
- mockHttpClient = MockClient((_) async => http.Response(commitsListJson, HttpStatus.ok));
- gerritService = GerritService(config: FakeConfig(), httpClient: mockHttpClient);
- final Iterable<GerritCommit> commits = await gerritService.commits(Config.recipesSlug, 'main');
- expect(commits.length, 1);
- final GerritCommit commit = commits.single;
- expect(commit.author?.email, 'dash@flutter.dev');
- expect(commit.author?.name, 'Dash');
- expect(commit.author?.time, isNotNull);
- expect(commit.committer?.email, 'flutter-scoped@luci-project-accounts.iam.gserviceaccount.com');
- expect(commit.committer?.name, 'CQ Bot Account');
- expect(commit.committer?.time, isNotNull);
- final DateTime time = commit.author!.time!;
- final DateTime expectedTime = DateTime(2023, 4, 20, 18, 00, 14);
- expect(time, expectedTime);
- });
- });
-
- group('getCommit', () {
- test('Returns a commit', () async {
- mockHttpClient = MockClient((_) async => http.Response(getCommitJson, HttpStatus.ok));
- gerritService = GerritService(config: FakeConfig(), httpClient: mockHttpClient);
- final GerritCommit? commit = await gerritService.getCommit(
- RepositorySlug('flutter', 'flutter'),
- '7a702db7c1c8dc057d95e0d23849c885b3463ff3',
- );
- expect(commit, isNotNull);
- expect(commit!.author?.email, 'dash@flutter.dev');
- expect(commit.author?.name, 'Dash');
- expect(commit.author?.time, isNotNull);
- final DateTime time = commit.author!.time!;
- final DateTime expectedTime = DateTime(2023, 4, 20, 18, 0, 14);
- expect(time, expectedTime);
- });
-
- test('Uses percent-encoding for project name', () async {
- mockHttpClient = MockClient((request) async {
- expect(request.url.path, startsWith('/projects/mirrors%2Fflutter'));
- return http.Response(getCommitJson, HttpStatus.ok);
- });
- gerritService = GerritService(config: FakeConfig(), httpClient: mockHttpClient);
- final commit = await gerritService.getCommit(
- RepositorySlug('flutter', 'mirrors/flutter'),
- '7a702db7c1c8dc057d95e0d23849c885b3463ff3',
- );
- expect(commit, isNotNull);
- });
- });
-
- group('findMirroredCommit', () {
- test('Uses matching slug for GoB mirror', () async {
- mockHttpClient = MockClient((request) async {
- expect(request.url.path, startsWith('/projects/mirrors%2Fpackages'));
- return http.Response(getCommitJson, HttpStatus.ok);
- });
- gerritService = GerritService(config: FakeConfig(), httpClient: mockHttpClient);
- final commit = await gerritService.findMirroredCommit(
- RepositorySlug('flutter', 'packages'),
- '7a702db7c1c8dc057d95e0d23849c885b3463ff3',
- );
- expect(commit, isNotNull);
- });
- });
-
- group('createBranch', () {
- test('ok response', () async {
- mockHttpClient = MockClient((_) async => http.Response(createBranchJson, HttpStatus.ok));
- gerritService = GerritService(
- config: FakeConfig(),
- httpClient: mockHttpClient,
- authClientProvider: ({
- Client? baseClient,
- required List<String> scopes,
- }) async =>
- FakeAuthClient(baseClient!),
- retryDelay: Duration.zero,
- );
-
- await gerritService.createBranch(
- Config.recipesSlug,
- 'flutter-2.13-candidate.0',
- '00439ab49a991db42595f14078adb9811a6f60c6',
- );
- });
-
- test('unexpected response', () async {
- mockHttpClient = MockClient((_) async => http.Response(createBranchJson, HttpStatus.ok));
- gerritService = GerritService(
- config: FakeConfig(),
- httpClient: mockHttpClient,
- authClientProvider: ({
- Client? baseClient,
- required List<String> scopes,
- }) async =>
- FakeAuthClient(baseClient!),
- retryDelay: Duration.zero,
- );
- expect(
- () async => gerritService.createBranch(Config.recipesSlug, 'flutter-2.13-candidate.0', 'abc'),
- throwsExceptionWith<InternalServerError>('Failed to create branch'),
- );
- });
-
- test('retries non-200 responses', () async {
- int attempts = 0;
- mockHttpClient = MockClient((_) async {
- attempts = attempts + 1;
- // Only send a failed response on the first attempt
- if (attempts == 1) {
- return http.Response('error', HttpStatus.internalServerError);
- }
- return http.Response(createBranchJson, HttpStatus.accepted);
- });
- gerritService = GerritService(
- config: FakeConfig(),
- httpClient: mockHttpClient,
- authClientProvider: ({
- Client? baseClient,
- required List<String> scopes,
- }) async =>
- FakeAuthClient(baseClient!),
- retryDelay: Duration.zero,
- );
- await gerritService.createBranch(
- Config.recipesSlug,
- 'flutter-2.13-candidate.0',
- '00439ab49a991db42595f14078adb9811a6f60c6',
- );
- expect(attempts, 2);
- });
- });
-}
-
-const String commitsListJson = ''')]}'
-{
- "log": [
- {
- "commit": "7a702db7c1c8dc057d95e0d23849c885b3463ff3",
- "tree": "9c1529c1e92b3431534dcbf01d2b63547b73b005",
- "parents": [
- "289edf3f90678e7ce1319aa1e8a57d7506c461d1"
- ],
- "author": {
- "name": "Dash",
- "email": "dash@flutter.dev",
- "time": "Tue Apr 20 18:00:14 2023 +0000"
- },
- "committer": {
- "name": "CQ Bot Account",
- "email": "flutter-scoped@luci-project-accounts.iam.gserviceaccount.com",
- "time": "Tue Apr 20 18:00:14 2023 +0000"
- },
- "message": "My first recipe change\\n\\ntested through `led get-builder"
- }
- ],
- "next": "00439ab49a991db42595f14078adb9811a6f60c6"
-}
-''';
-
-const String createBranchJson = ''')]}'
-{
- "revision": "00439ab49a991db42595f14078adb9811a6f60c6"
-}
-''';
-
-const String getCommitJson = ''')]}'
-{
- "commit": "7a702db7c1c8dc057d95e0d23849c885b3463ff3",
- "parents": [
- {
- "commit": "1eee2c9d8f352483781e772f35dc586a69ff5646",
- "subject": "Migrate contributor agreements to All-Projects."
- }
- ],
- "author": {
- "name": "Dash",
- "email": "dash@flutter.dev",
- "time": "Wed Apr 20 18:00:14 2023 +0000"
- },
- "committer": {
- "name": "CQ Bot Account",
- "email": "flutter-scoped@luci-project-accounts.iam.gserviceaccount.com",
- "time": "Wed Apr 20 18:00:14 2023 +0000"
- },
- "subject": "Use an EventBus to manage star icons",
- "message": "Use an EventBus to manage star icons\\n\\nImage widgets that need to ..."
-}
-''';
diff --git a/app_dart/test/service/github_checks_service_test.dart b/app_dart/test/service/github_checks_service_test.dart
deleted file mode 100644
index a8ef805..0000000
--- a/app_dart/test/service/github_checks_service_test.dart
+++ /dev/null
@@ -1,286 +0,0 @@
-// Copyright 2020 The Flutter 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:convert';
-
-import 'package:cocoon_service/src/model/ci_yaml/target.dart';
-import 'package:cocoon_service/src/model/luci/buildbucket.dart';
-import 'package:cocoon_service/src/model/luci/push_message.dart' as push_message;
-import 'package:cocoon_service/src/service/github_checks_service.dart';
-
-import 'package:github/github.dart' as github;
-import 'package:github/github.dart';
-import 'package:github/hooks.dart';
-import 'package:mockito/mockito.dart';
-import 'package:test/test.dart';
-
-import '../model/github/checks_test_data.dart';
-import '../src/datastore/fake_config.dart';
-import '../src/service/fake_scheduler.dart';
-import '../src/utilities/entity_generators.dart';
-import '../src/utilities/mocks.dart';
-
-void main() {
- FakeConfig config;
- late FakeScheduler scheduler;
- MockGithubService mockGithubService;
- late MockGithubChecksUtil mockGithubChecksUtil;
- late MockLuciBuildService mockLuciBuildService;
- late GithubChecksService githubChecksService;
- late github.CheckRun checkRun;
- late RepositorySlug slug;
-
- setUp(() {
- mockGithubService = MockGithubService();
- mockLuciBuildService = MockLuciBuildService();
- when(mockGithubService.listFiles(any)).thenAnswer((_) async => <String>[]);
- mockGithubChecksUtil = MockGithubChecksUtil();
- config = FakeConfig(githubService: mockGithubService, rollerAccountsValue: {'engine-flutter-autoroll'});
- githubChecksService = GithubChecksService(
- config,
- githubChecksUtil: mockGithubChecksUtil,
- );
- slug = RepositorySlug('flutter', 'cocoon');
- scheduler = FakeScheduler(
- config: config,
- luciBuildService: mockLuciBuildService,
- githubChecksUtil: mockGithubChecksUtil,
- ciYaml: exampleConfig,
- );
- checkRun = github.CheckRun.fromJson(
- jsonDecode(
- '{"name": "Linux Coverage", "id": 123, "external_id": "678", "status": "completed", "started_at": "2020-05-10T02:49:31Z", "head_sha": "the_sha", "check_suite": {"id": 456}}',
- ) as Map<String, dynamic>,
- );
- final Map<String, github.CheckRun> checkRuns = <String, github.CheckRun>{'Cocoon': checkRun};
- when(mockGithubChecksUtil.allCheckRuns(any, any)).thenAnswer((_) async {
- return checkRuns;
- });
- });
-
- group('handleCheckSuiteEvent', () {
- test('requested triggers all builds', () async {
- final CheckSuiteEvent checkSuiteEvent =
- CheckSuiteEvent.fromJson(jsonDecode(checkSuiteString) as Map<String, dynamic>);
- when(mockGithubChecksUtil.createCheckRun(any, any, any, any, output: anyNamed('output')))
- .thenAnswer((_) async => generateCheckRun(1));
- final PullRequest pullRequest = generatePullRequest(id: 758);
- await githubChecksService.handleCheckSuite(pullRequest, checkSuiteEvent, scheduler);
- final List<Target> scheduledTargets = verify(
- mockLuciBuildService.scheduleTryBuilds(
- targets: captureAnyNamed('targets'),
- pullRequest: anyNamed('pullRequest'),
- checkSuiteEvent: anyNamed('checkSuiteEvent'),
- ),
- ).captured.single as List<Target>;
- final Iterable<String> scheduledTargetNames = scheduledTargets.map((Target target) => target.value.name);
- expect(scheduledTargetNames, <String>[
- 'Linux A',
- 'Mac A',
- 'Windows A',
- ]);
- });
- });
-
- group('updateCheckStatus', () {
- test('Userdata is empty', () async {
- final push_message.BuildPushMessage buildMessage =
- push_message.BuildPushMessage.fromJson(jsonDecode(buildPushMessageJsonTemplate('')) as Map<String, dynamic>);
- final bool success = await githubChecksService.updateCheckStatus(buildMessage, mockLuciBuildService, slug);
- expect(success, isFalse);
- });
- test('Userdata does not contain check_run_id', () async {
- final push_message.BuildPushMessage buildMessage = push_message.BuildPushMessage.fromJson(
- jsonDecode(buildPushMessageJsonTemplate('{\\"retries\\": 1}')) as Map<String, dynamic>,
- );
- final bool success = await githubChecksService.updateCheckStatus(buildMessage, mockLuciBuildService, slug);
- expect(success, isFalse);
- });
- test('Userdata contain check_run_id', () async {
- when(mockGithubChecksUtil.getCheckRun(any, any, any)).thenAnswer((_) async => checkRun);
- when(
- mockLuciBuildService.getBuildById(
- '8905920700440101120',
- fields: 'id,builder,summaryMarkdown',
- ),
- ).thenAnswer(
- (_) async => const Build(
- id: '8905920700440101120',
- builderId: BuilderId(bucket: 'luci.flutter.prod', project: 'flutter', builder: 'Linux Coverage'),
- summaryMarkdown: 'test summary',
- ),
- );
- final push_message.BuildPushMessage buildPushMessage = push_message.BuildPushMessage.fromJson(
- jsonDecode(
- buildPushMessageJsonTemplate('{\\"check_run_id\\": 123,'
- '\\"repo_owner\\": \\"flutter\\",'
- '\\"repo_name\\": \\"cocoon\\"}'),
- ) as Map<String, dynamic>,
- );
- await githubChecksService.updateCheckStatus(buildPushMessage, mockLuciBuildService, slug);
- final github.CheckRun checkRunCaptured = await verify(
- mockGithubChecksUtil.updateCheckRun(
- any,
- any,
- captureAny,
- status: anyNamed('status'),
- conclusion: anyNamed('conclusion'),
- detailsUrl: anyNamed('detailsUrl'),
- output: anyNamed('output'),
- ),
- ).captured.first;
- expect(checkRunCaptured.id, checkRun.id);
- expect(checkRunCaptured.name, checkRun.name);
- });
- test('Should rerun a failed task for a roller account', () async {
- when(mockGithubChecksUtil.getCheckRun(any, any, any)).thenAnswer((_) async => checkRun);
- final push_message.BuildPushMessage buildPushMessage = push_message.BuildPushMessage.fromJson(
- jsonDecode(
- buildPushMessageJsonTemplate('{\\"check_run_id\\": 1,'
- '\\"repo_owner\\": \\"flutter\\",'
- '\\"repo_name\\": \\"cocoon\\",'
- '\\"user_login\\": \\"engine-flutter-autoroll\\"}'),
- ) as Map<String, dynamic>,
- );
- when(
- mockLuciBuildService.rescheduleBuild(
- builderName: 'Linux Coverage',
- buildPushMessage: buildPushMessage,
- rescheduleAttempt: 1,
- ),
- ).thenAnswer(
- (_) async => const Build(
- id: '8905920700440101120',
- builderId: BuilderId(bucket: 'luci.flutter.prod', project: 'flutter', builder: 'Linux Coverage'),
- ),
- );
- expect(checkRun.status, github.CheckRunStatus.completed);
- await githubChecksService.updateCheckStatus(buildPushMessage, mockLuciBuildService, slug, rescheduled: true);
- final List<dynamic> captured = verify(
- mockGithubChecksUtil.updateCheckRun(
- any,
- any,
- captureAny,
- status: captureAnyNamed('status'),
- conclusion: captureAnyNamed('conclusion'),
- detailsUrl: anyNamed('detailsUrl'),
- output: anyNamed('output'),
- ),
- ).captured;
- expect(captured.length, 3);
- expect(captured[1], github.CheckRunStatus.queued);
- expect(captured[2], isNull);
- });
- test('Should not rerun a failed task for a non roller account', () async {
- when(mockGithubChecksUtil.getCheckRun(any, any, any)).thenAnswer((_) async => checkRun);
- final push_message.BuildPushMessage buildPushMessage = push_message.BuildPushMessage.fromJson(
- jsonDecode(
- buildPushMessageJsonTemplate('{\\"check_run_id\\": 1,'
- '\\"repo_owner\\": \\"flutter\\",'
- '\\"repo_name\\": \\"cocoon\\",'
- '\\"user_login\\": \\"test-account\\"}'),
- ) as Map<String, dynamic>,
- );
- when(
- mockLuciBuildService.rescheduleBuild(
- builderName: 'Linux Coverage',
- buildPushMessage: buildPushMessage,
- rescheduleAttempt: 1,
- ),
- ).thenAnswer(
- (_) async => const Build(
- id: '8905920700440101120',
- builderId: BuilderId(bucket: 'luci.flutter.prod', project: 'flutter', builder: 'Linux Coverage'),
- ),
- );
- when(
- mockLuciBuildService.getBuildById(
- '8905920700440101120',
- fields: 'id,builder,summaryMarkdown',
- ),
- ).thenAnswer(
- (_) async => const Build(
- id: '8905920700440101120',
- builderId: BuilderId(bucket: 'luci.flutter.prod', project: 'flutter', builder: 'Linux Coverage'),
- summaryMarkdown: 'test summary',
- ),
- );
- await githubChecksService.updateCheckStatus(buildPushMessage, mockLuciBuildService, slug);
- final List<dynamic> captured = verify(
- mockGithubChecksUtil.updateCheckRun(
- any,
- any,
- any,
- status: captureAnyNamed('status'),
- conclusion: captureAnyNamed('conclusion'),
- detailsUrl: anyNamed('detailsUrl'),
- output: captureAnyNamed('output'),
- ),
- ).captured;
- expect(captured.length, 3);
- expect(captured[0], github.CheckRunStatus.completed);
- expect(captured[1], github.CheckRunConclusion.failure);
- });
- });
-
- group('getGithubSummary', () {
- test('nonempty summaryMarkdown', () async {
- const String summaryMarkdown = 'test';
- const String expectedSummary = '$kGithubSummary$summaryMarkdown';
- expect(githubChecksService.getGithubSummary(summaryMarkdown), expectedSummary);
- });
-
- test('empty summaryMarkdown', () async {
- const String expectedSummary = '${kGithubSummary}Empty summaryMarkdown';
- expect(githubChecksService.getGithubSummary(null), expectedSummary);
- });
-
- test('really large summaryMarkdown', () async {
- String summaryMarkdown = '';
- for (int i = 0; i < 20000; i++) {
- summaryMarkdown += 'test ';
- }
- expect(githubChecksService.getGithubSummary(summaryMarkdown), startsWith('$kGithubSummary[TRUNCATED...]'));
- expect(githubChecksService.getGithubSummary(summaryMarkdown).length, lessThan(65535));
- });
- });
-}
-
-String buildPushMessageJsonTemplate(String jsonUserData) => '''{
- "build": {
- "bucket": "luci.flutter.prod",
- "canary": false,
- "canary_preference": "PROD",
- "created_by": "user:dnfield@google.com",
- "created_ts": "1565049186247524",
- "experimental": true,
- "id": "8905920700440101120",
- "parameters_json": "{\\"builder_name\\": \\"Linux Coverage\\", \\"properties\\": {\\"git_ref\\": \\"refs/pull/37647/head\\", \\"git_url\\": \\"https://github.com/flutter/flutter\\"}}",
- "project": "flutter",
- "result_details_json": "{\\"properties\\": {}, \\"swarming\\": {\\"bot_dimensions\\": {\\"caches\\": [\\"flutter_openjdk_install\\", \\"git\\", \\"goma_v2\\", \\"vpython\\"], \\"cores\\": [\\"8\\"], \\"cpu\\": [\\"x86\\", \\"x86-64\\", \\"x86-64-Broadwell_GCE\\", \\"x86-64-avx2\\"], \\"gce\\": [\\"1\\"], \\"gpu\\": [\\"none\\"], \\"id\\": [\\"luci-flutter-prod-xenial-2-bnrz\\"], \\"image\\": [\\"chrome-xenial-19052201-9cb74617499\\"], \\"inside_docker\\": [\\"0\\"], \\"kvm\\": [\\"1\\"], \\"locale\\": [\\"en_US.UTF-8\\"], \\"machine_type\\": [\\"n1-standard-8\\"], \\"os\\": [\\"Linux\\", \\"Ubuntu\\", \\"Ubuntu-16.04\\"], \\"pool\\": [\\"luci.flutter.prod\\"], \\"python\\": [\\"2.7.12\\"], \\"server_version\\": [\\"4382-5929880\\"], \\"ssd\\": [\\"0\\"], \\"zone\\": [\\"us\\", \\"us-central\\", \\"us-central1\\", \\"us-central1-c\\"]}}}",
- "service_account": "flutter-prod-builder@chops-service-accounts.iam.gserviceaccount.com",
- "started_ts": "1565049193786080",
- "status": "COMPLETED",
- "result": "FAILURE",
- "status_changed_ts": "1565049194386647",
- "tags": [
- "build_address:luci.flutter.prod/Linux Coverage/1698",
- "builder:Linux Coverage",
- "buildset:pr/git/37647",
- "buildset:sha/git/0d78fc94f890a64af140ce0a2671ac5fc636f59b",
- "swarming_hostname:chromium-swarm.appspot.com",
- "swarming_tag:log_location:logdog://logs.chromium.org/flutter/buildbucket/cr-buildbucket.appspot.com/8905920700440101120/+/annotations",
- "swarming_tag:luci_project:flutter",
- "swarming_tag:os:Linux",
- "swarming_tag:recipe_name:flutter/flutter",
- "swarming_tag:recipe_package:infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",
- "swarming_task_id:467d04f2f022d510"
- ],
- "updated_ts": "1565049194391321",
- "url": "https://ci.chromium.org/b/8905920700440101120",
- "utcnow_ts": "1565049194653640"
- },
- "hostname": "cr-buildbucket.appspot.com",
- "user_data": "$jsonUserData"
-}''';
diff --git a/app_dart/test/service/github_service_test.dart b/app_dart/test/service/github_service_test.dart
deleted file mode 100644
index 405c2c8..0000000
--- a/app_dart/test/service/github_service_test.dart
+++ /dev/null
@@ -1,110 +0,0 @@
-// Copyright 2020 The Flutter 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:convert';
-import 'dart:io';
-
-import 'package:cocoon_service/src/service/github_service.dart';
-
-import 'package:github/github.dart';
-import 'package:http/http.dart' as http;
-import 'package:mockito/mockito.dart';
-import 'package:test/test.dart';
-
-import '../src/utilities/mocks.dart';
-
-void main() {
- late GithubService githubService;
- MockGitHub mockGitHub;
- late RepositorySlug slug;
-
- const String branch = 'master';
- const int lastCommitTimestampMills = 100;
-
- const String authorName = 'Jane Doe';
- const String authorEmail = 'janedoe@example.com';
- const String authorDate = '2000-01-01T10:10:10Z';
- const String authorLogin = 'Username';
- const String authorAvatarUrl = 'http://example.com/avatar';
- const String commitMessage = 'commit message';
-
- List<String> shas;
-
- setUp(() {
- shas = <String>[];
- mockGitHub = MockGitHub();
- githubService = GithubService(mockGitHub);
- slug = RepositorySlug('flutter', 'flutter');
- final PostExpectation<Future<http.Response>> whenGithubRequest = when(
- mockGitHub.request(
- 'GET',
- '/repos/${slug.owner}/${slug.name}/commits',
- headers: anyNamed('headers'),
- params: anyNamed('params'),
- body: anyNamed('body'),
- statusCode: anyNamed('statusCode'),
- ),
- );
- whenGithubRequest.thenAnswer((_) async {
- final List<dynamic> data = <dynamic>[];
- for (String sha in shas) {
- // https://developer.github.com/v3/repos/commits/#list-commits
- data.add(<String, dynamic>{
- 'sha': sha,
- 'commit': <String, dynamic>{
- 'message': commitMessage,
- 'author': <String, dynamic>{
- 'name': authorName,
- 'email': authorEmail,
- 'date': authorDate,
- },
- },
- 'author': <String, dynamic>{
- 'login': authorLogin,
- 'avatar_url': authorAvatarUrl,
- },
- });
- }
- return http.Response(json.encode(data), HttpStatus.ok);
- });
- });
-
- test('listCommits decodes all relevant fields of each commit', () async {
- shas = <String>['1'];
- final List<RepositoryCommit> commits = await githubService.listBranchedCommits(
- slug,
- branch,
- lastCommitTimestampMills,
- );
- expect(commits, hasLength(1));
- final RepositoryCommit commit = commits.single;
- expect(commit.sha, shas.single);
- expect(commit.author, isNotNull);
- expect(commit.author!.login, authorLogin);
- expect(commit.author!.avatarUrl, authorAvatarUrl);
- expect(commit.commit, isNotNull);
- expect(commit.commit!.message, commitMessage);
- expect(commit.commit!.committer, isNotNull);
- expect(commit.commit!.committer!.name, authorName);
- expect(commit.commit!.committer!.email, authorEmail);
- });
-
- test('searchIssuesAndPRs encodes query properly', () async {
- mockGitHub = MockGitHub();
- final mockSearchService = MockSearchService();
- when(mockGitHub.search).thenReturn(mockSearchService);
- when(mockSearchService.issues(any)).thenAnswer((invocation) {
- expect(
- invocation.positionalArguments[0],
- '6afa96d84e2ecf6537f8ea76341d8ba397942e80%20repo%3Aflutter%2Fflutter',
- );
- return const Stream.empty();
- });
- githubService = GithubService(mockGitHub);
- await githubService.searchIssuesAndPRs(
- slug,
- '6afa96d84e2ecf6537f8ea76341d8ba397942e80',
- );
- });
-}
diff --git a/app_dart/test/service/luci_build_service_test.dart b/app_dart/test/service/luci_build_service_test.dart
deleted file mode 100644
index 79e2d96..0000000
--- a/app_dart/test/service/luci_build_service_test.dart
+++ /dev/null
@@ -1,1070 +0,0 @@
-// Copyright 2020 The Flutter 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:convert';
-import 'dart:core';
-
-import 'package:cocoon_service/cocoon_service.dart';
-import 'package:cocoon_service/src/model/appengine/commit.dart';
-import 'package:cocoon_service/src/model/appengine/task.dart';
-import 'package:cocoon_service/src/model/ci_yaml/target.dart';
-import 'package:cocoon_service/src/model/luci/buildbucket.dart';
-import 'package:cocoon_service/src/model/luci/push_message.dart' as push_message;
-import 'package:cocoon_service/src/service/exceptions.dart';
-import 'package:cocoon_service/src/service/datastore.dart';
-import 'package:github/github.dart';
-import 'package:cocoon_service/src/model/github/checks.dart' as cocoon_checks;
-import 'package:mockito/mockito.dart';
-import 'package:test/test.dart';
-
-import '../src/datastore/fake_config.dart';
-import '../src/request_handling/fake_pubsub.dart';
-import '../src/service/fake_gerrit_service.dart';
-import '../src/service/fake_github_service.dart';
-import '../src/utilities/entity_generators.dart';
-import '../src/utilities/mocks.dart';
-import '../src/utilities/push_message.dart';
-import '../src/utilities/webhook_generators.dart';
-
-void main() {
- late CacheService cache;
- late FakeConfig config;
- FakeGithubService githubService;
- late MockBuildBucketClient mockBuildBucketClient;
- late LuciBuildService service;
- late RepositorySlug slug;
- late MockGithubChecksUtil mockGithubChecksUtil = MockGithubChecksUtil();
- late FakePubSub pubsub;
-
- final List<Target> targets = <Target>[
- generateTarget(1, properties: <String, String>{'os': 'abc'}),
- ];
- final PullRequest pullRequest = generatePullRequest(id: 1, repo: 'cocoon');
-
- group('getBuilds', () {
- final Build macBuild = generateBuild(999, name: 'Mac', status: Status.started);
- final Build linuxBuild = generateBuild(998, name: 'Linux', bucket: 'try', status: Status.started);
-
- setUp(() {
- cache = CacheService(inMemory: true);
- githubService = FakeGithubService();
- config = FakeConfig(githubService: githubService);
- mockBuildBucketClient = MockBuildBucketClient();
- pubsub = FakePubSub();
- service = LuciBuildService(
- config: config,
- cache: cache,
- buildBucketClient: mockBuildBucketClient,
- gerritService: FakeGerritService(),
- pubsub: pubsub,
- );
- slug = RepositorySlug('flutter', 'cocoon');
- });
-
- test('Null build', () async {
- when(mockBuildBucketClient.batch(any)).thenAnswer((_) async {
- return BatchResponse(
- responses: <Response>[
- Response(
- searchBuilds: SearchBuildsResponse(
- builds: <Build>[macBuild],
- ),
- ),
- ],
- );
- });
- final Iterable<Build> builds = await service.getTryBuilds(
- Config.flutterSlug,
- 'shasha',
- 'abcd',
- );
- expect(builds.first, macBuild);
- });
-
- test('Existing prod build', () async {
- when(mockBuildBucketClient.batch(any)).thenAnswer((_) async {
- return const BatchResponse(
- responses: <Response>[
- Response(
- searchBuilds: SearchBuildsResponse(
- builds: <Build>[],
- ),
- ),
- ],
- );
- });
- final Iterable<Build> builds = await service.getProdBuilds(
- slug,
- 'commit123',
- 'abcd',
- );
- expect(builds, isEmpty);
- });
-
- test('Existing try build', () async {
- when(mockBuildBucketClient.batch(any)).thenAnswer((_) async {
- return BatchResponse(
- responses: <Response>[
- Response(
- searchBuilds: SearchBuildsResponse(
- builds: <Build>[linuxBuild],
- ),
- ),
- ],
- );
- });
- final Iterable<Build> builds = await service.getTryBuilds(
- Config.flutterSlug,
- 'shasha',
- 'abcd',
- );
- expect(builds.first, linuxBuild);
- });
-
- test('Existing try build by pull request', () async {
- when(mockBuildBucketClient.batch(any)).thenAnswer((_) async {
- return BatchResponse(
- responses: <Response>[
- Response(
- searchBuilds: SearchBuildsResponse(
- builds: <Build>[linuxBuild],
- ),
- ),
- ],
- );
- });
- final Iterable<Build> builds = await service.getTryBuildsByPullRequest(
- PullRequest(id: 998, base: PullRequestHead(repo: Repository(fullName: 'flutter/cocoon'))),
- );
- expect(builds.first, linuxBuild);
- });
- });
-
- group('getBuilders', () {
- setUp(() {
- cache = CacheService(inMemory: true);
- githubService = FakeGithubService();
- config = FakeConfig(githubService: githubService);
- mockBuildBucketClient = MockBuildBucketClient();
- pubsub = FakePubSub();
- service = LuciBuildService(
- config: config,
- cache: cache,
- buildBucketClient: mockBuildBucketClient,
- gerritService: FakeGerritService(),
- pubsub: pubsub,
- );
- slug = RepositorySlug('flutter', 'flutter');
- });
-
- test('with one rpc call', () async {
- when(mockBuildBucketClient.listBuilders(any)).thenAnswer((_) async {
- return const ListBuildersResponse(
- builders: [
- BuilderItem(id: BuilderId(bucket: 'prod', project: 'flutter', builder: 'test1')),
- BuilderItem(id: BuilderId(bucket: 'prod', project: 'flutter', builder: 'test2')),
- ],
- );
- });
- final Set<String> builders = await service.getAvailableBuilderSet();
- expect(builders.length, 2);
- expect(builders.contains('test1'), isTrue);
- });
-
- test('with more than one rpc calls', () async {
- int retries = -1;
- when(mockBuildBucketClient.listBuilders(any)).thenAnswer((_) async {
- retries++;
- if (retries == 0) {
- return const ListBuildersResponse(
- builders: [
- BuilderItem(id: BuilderId(bucket: 'prod', project: 'flutter', builder: 'test1')),
- BuilderItem(id: BuilderId(bucket: 'prod', project: 'flutter', builder: 'test2')),
- ],
- nextPageToken: 'token',
- );
- } else if (retries == 1) {
- return const ListBuildersResponse(
- builders: [
- BuilderItem(id: BuilderId(bucket: 'prod', project: 'flutter', builder: 'test3')),
- BuilderItem(id: BuilderId(bucket: 'prod', project: 'flutter', builder: 'test4')),
- ],
- );
- } else {
- return const ListBuildersResponse(builders: []);
- }
- });
- final Set<String> builders = await service.getAvailableBuilderSet();
- expect(builders.length, 4);
- expect(builders, <String>{'test1', 'test2', 'test3', 'test4'});
- });
- });
-
- group('buildsForRepositoryAndPr', () {
- final Build macBuild = generateBuild(999, name: 'Mac', status: Status.started);
- final Build linuxBuild = generateBuild(998, name: 'Linux', status: Status.started);
-
- setUp(() {
- cache = CacheService(inMemory: true);
- githubService = FakeGithubService();
- config = FakeConfig(githubService: githubService);
- mockBuildBucketClient = MockBuildBucketClient();
- pubsub = FakePubSub();
- service = LuciBuildService(
- config: config,
- cache: cache,
- buildBucketClient: mockBuildBucketClient,
- pubsub: pubsub,
- );
- slug = RepositorySlug('flutter', 'cocoon');
- });
-
- test('Empty responses are handled correctly', () async {
- when(mockBuildBucketClient.batch(any)).thenAnswer((_) async {
- return const BatchResponse(
- responses: <Response>[
- Response(
- searchBuilds: SearchBuildsResponse(
- builds: <Build>[],
- ),
- ),
- ],
- );
- });
- final Iterable<Build> builds = await service.getTryBuilds(
- RepositorySlug.full(pullRequest.base!.repo!.fullName),
- pullRequest.head!.sha!,
- null,
- );
- expect(builds, isEmpty);
- });
-
- test('Response returning a couple of builds', () async {
- when(mockBuildBucketClient.batch(any)).thenAnswer((_) async {
- return BatchResponse(
- responses: <Response>[
- Response(
- searchBuilds: SearchBuildsResponse(
- builds: <Build>[macBuild],
- ),
- ),
- Response(
- searchBuilds: SearchBuildsResponse(
- builds: <Build>[linuxBuild],
- ),
- ),
- ],
- );
- });
- final Iterable<Build> builds = await service.getTryBuilds(
- RepositorySlug.full(pullRequest.base!.repo!.fullName),
- pullRequest.head!.sha!,
- null,
- );
- expect(builds, equals(<Build>{macBuild, linuxBuild}));
- });
- });
-
- group('scheduleBuilds', () {
- setUp(() {
- cache = CacheService(inMemory: true);
- githubService = FakeGithubService();
- config = FakeConfig(githubService: githubService);
- mockBuildBucketClient = MockBuildBucketClient();
- mockGithubChecksUtil = MockGithubChecksUtil();
- pubsub = FakePubSub();
- service = LuciBuildService(
- config: config,
- cache: cache,
- buildBucketClient: mockBuildBucketClient,
- githubChecksUtil: mockGithubChecksUtil,
- gerritService: FakeGerritService(branchesValue: <String>['master']),
- pubsub: pubsub,
- );
- slug = RepositorySlug('flutter', 'cocoon');
- });
-
- test('schedule try builds successfully', () async {
- final PullRequest pullRequest = generatePullRequest();
- when(mockBuildBucketClient.batch(any)).thenAnswer((_) async {
- return BatchResponse(
- responses: <Response>[
- Response(
- scheduleBuild: generateBuild(1),
- ),
- ],
- );
- });
- when(mockGithubChecksUtil.createCheckRun(any, any, any, any))
- .thenAnswer((_) async => generateCheckRun(1, name: 'Linux 1'));
- final List<Target> scheduledTargets = await service.scheduleTryBuilds(
- pullRequest: pullRequest,
- targets: targets,
- );
- final Iterable<String> scheduledTargetNames = scheduledTargets.map((Target target) => target.value.name);
- expect(scheduledTargetNames, <String>['Linux 1']);
- final BatchRequest batchRequest = pubsub.messages.single as BatchRequest;
- expect(batchRequest.requests!.single.scheduleBuild, isNotNull);
-
- final ScheduleBuildRequest scheduleBuild = batchRequest.requests!.single.scheduleBuild!;
- expect(scheduleBuild.builderId.bucket, 'try');
- expect(scheduleBuild.builderId.builder, 'Linux 1');
- expect(scheduleBuild.notify?.pubsubTopic, 'projects/flutter-dashboard/topics/luci-builds');
- final Map<String, dynamic> userData =
- jsonDecode(String.fromCharCodes(base64Decode(scheduleBuild.notify!.userData!))) as Map<String, dynamic>;
- expect(userData, <String, dynamic>{
- 'repo_owner': 'flutter',
- 'repo_name': 'flutter',
- 'user_agent': 'flutter-cocoon',
- 'check_run_id': 1,
- 'commit_sha': 'abc',
- 'commit_branch': 'master',
- 'builder_name': 'Linux 1',
- });
-
- final Map<String, dynamic> properties = scheduleBuild.properties!;
- final List<RequestedDimension> dimensions = scheduleBuild.dimensions!;
- expect(properties, <String, dynamic>{
- 'os': 'abc',
- 'dependencies': <dynamic>[],
- 'bringup': false,
- 'git_branch': 'master',
- 'git_url': 'https://github.com/flutter/flutter',
- 'git_ref': 'refs/pull/123/head',
- 'exe_cipd_version': 'refs/heads/main',
- });
- expect(dimensions.length, 1);
- expect(dimensions[0].key, 'os');
- expect(dimensions[0].value, 'abc');
- });
-
- test('schedule try builds with github build labels successfully', () async {
- final issueLabels = [
- IssueLabel(name: '${LuciBuildService.githubBuildLabelPrefix}hello'),
- IssueLabel(name: '${LuciBuildService.githubBuildLabelPrefix}world'),
- ];
- final PullRequest pullRequest = generatePullRequest(labels: issueLabels);
- when(mockBuildBucketClient.batch(any)).thenAnswer((_) async {
- return BatchResponse(
- responses: <Response>[
- Response(
- scheduleBuild: generateBuild(1),
- ),
- ],
- );
- });
- when(mockGithubChecksUtil.createCheckRun(any, any, any, any))
- .thenAnswer((_) async => generateCheckRun(1, name: 'Linux 1'));
- final List<Target> scheduledTargets = await service.scheduleTryBuilds(
- pullRequest: pullRequest,
- targets: targets,
- );
- final Iterable<String> scheduledTargetNames = scheduledTargets.map((Target target) => target.value.name);
- expect(scheduledTargetNames, <String>['Linux 1']);
- final BatchRequest batchRequest = pubsub.messages.single as BatchRequest;
- expect(batchRequest.requests!.single.scheduleBuild, isNotNull);
-
- final ScheduleBuildRequest scheduleBuild = batchRequest.requests!.single.scheduleBuild!;
- expect(scheduleBuild.builderId.bucket, 'try');
- expect(scheduleBuild.builderId.builder, 'Linux 1');
- expect(scheduleBuild.notify?.pubsubTopic, 'projects/flutter-dashboard/topics/luci-builds');
- final Map<String, dynamic> userData =
- jsonDecode(String.fromCharCodes(base64Decode(scheduleBuild.notify!.userData!))) as Map<String, dynamic>;
- expect(userData, <String, dynamic>{
- 'repo_owner': 'flutter',
- 'repo_name': 'flutter',
- 'user_agent': 'flutter-cocoon',
- 'check_run_id': 1,
- 'commit_sha': 'abc',
- 'commit_branch': 'master',
- 'builder_name': 'Linux 1',
- });
-
- final Map<String, dynamic> properties = scheduleBuild.properties!;
- final List<RequestedDimension> dimensions = scheduleBuild.dimensions!;
- expect(properties, <String, dynamic>{
- 'os': 'abc',
- 'dependencies': <dynamic>[],
- 'bringup': false,
- 'git_branch': 'master',
- 'git_url': 'https://github.com/flutter/flutter',
- 'git_ref': 'refs/pull/123/head',
- 'exe_cipd_version': 'refs/heads/main',
- LuciBuildService.propertiesGithubBuildLabelName: [
- '${LuciBuildService.githubBuildLabelPrefix}hello',
- '${LuciBuildService.githubBuildLabelPrefix}world',
- ],
- });
- expect(dimensions.length, 1);
- expect(dimensions[0].key, 'os');
- expect(dimensions[0].value, 'abc');
- });
-
- test('Schedule builds no-ops when targets list is empty', () async {
- await service.scheduleTryBuilds(
- pullRequest: pullRequest,
- targets: <Target>[],
- );
- verifyNever(mockGithubChecksUtil.createCheckRun(any, any, any, any));
- });
- });
-
- group('schedulePostsubmitBuilds', () {
- setUp(() {
- cache = CacheService(inMemory: true);
- mockBuildBucketClient = MockBuildBucketClient();
- pubsub = FakePubSub();
- service = LuciBuildService(
- config: FakeConfig(),
- cache: cache,
- buildBucketClient: mockBuildBucketClient,
- githubChecksUtil: mockGithubChecksUtil,
- pubsub: pubsub,
- );
- });
-
- test('schedule packages postsubmit builds successfully', () async {
- final Commit commit = generateCommit(0);
- when(mockGithubChecksUtil.createCheckRun(any, Config.packagesSlug, any, 'Linux 1'))
- .thenAnswer((_) async => generateCheckRun(1));
- when(mockBuildBucketClient.listBuilders(any)).thenAnswer((_) async {
- return const ListBuildersResponse(
- builders: [
- BuilderItem(id: BuilderId(bucket: 'prod', project: 'flutter', builder: 'Linux 1')),
- ],
- );
- });
- final Tuple<Target, Task, int> toBeScheduled = Tuple<Target, Task, int>(
- generateTarget(
- 1,
- properties: <String, String>{
- 'os': 'debian-10.12',
- },
- slug: Config.packagesSlug,
- ),
- generateTask(1),
- LuciBuildService.kDefaultPriority,
- );
- await service.schedulePostsubmitBuilds(
- commit: commit,
- toBeScheduled: <Tuple<Target, Task, int>>[
- toBeScheduled,
- ],
- );
- // Only one batch request should be published
- expect(pubsub.messages.length, 1);
- final BatchRequest request = pubsub.messages.first as BatchRequest;
- expect(request.requests?.single.scheduleBuild, isNotNull);
- final ScheduleBuildRequest scheduleBuild = request.requests!.single.scheduleBuild!;
- expect(scheduleBuild.builderId.bucket, 'prod');
- expect(scheduleBuild.builderId.builder, 'Linux 1');
- expect(scheduleBuild.notify?.pubsubTopic, 'projects/flutter-dashboard/topics/luci-builds-prod');
- final Map<String, dynamic> userData =
- jsonDecode(String.fromCharCodes(base64Decode(scheduleBuild.notify!.userData!))) as Map<String, dynamic>;
- expect(userData, <String, dynamic>{
- 'commit_key': 'flutter/flutter/master/1',
- 'task_key': '1',
- 'check_run_id': 1,
- 'commit_sha': '0',
- 'commit_branch': 'master',
- 'builder_name': 'Linux 1',
- 'repo_owner': 'flutter',
- 'repo_name': 'packages',
- });
- final Map<String, dynamic> properties = scheduleBuild.properties!;
- expect(properties, <String, dynamic>{
- 'dependencies': <dynamic>[],
- 'bringup': false,
- 'git_branch': 'master',
- 'exe_cipd_version': 'refs/heads/master',
- 'os': 'debian-10.12',
- });
- expect(scheduleBuild.exe, <String, String>{
- 'cipdVersion': 'refs/heads/master',
- });
- expect(scheduleBuild.dimensions, isNotEmpty);
- expect(
- scheduleBuild.dimensions!.singleWhere((RequestedDimension dimension) => dimension.key == 'os').value,
- 'debian-10.12',
- );
- });
-
- test('schedule postsubmit builds with correct userData for checkRuns', () async {
- when(mockGithubChecksUtil.createCheckRun(any, any, any, any))
- .thenAnswer((_) async => generateCheckRun(1, name: 'Linux 1'));
- final Commit commit = generateCommit(0, repo: 'packages');
- when(mockBuildBucketClient.listBuilders(any)).thenAnswer((_) async {
- return const ListBuildersResponse(
- builders: [
- BuilderItem(id: BuilderId(bucket: 'prod', project: 'flutter', builder: 'Linux 1')),
- ],
- );
- });
- final Tuple<Target, Task, int> toBeScheduled = Tuple<Target, Task, int>(
- generateTarget(
- 1,
- properties: <String, String>{
- 'os': 'debian-10.12',
- },
- slug: RepositorySlug('flutter', 'packages'),
- ),
- generateTask(1),
- LuciBuildService.kDefaultPriority,
- );
- await service.schedulePostsubmitBuilds(
- commit: commit,
- toBeScheduled: <Tuple<Target, Task, int>>[
- toBeScheduled,
- ],
- );
- // Only one batch request should be published
- expect(pubsub.messages.length, 1);
- final BatchRequest request = pubsub.messages.first as BatchRequest;
- expect(request.requests?.single.scheduleBuild, isNotNull);
- final ScheduleBuildRequest scheduleBuild = request.requests!.single.scheduleBuild!;
- expect(scheduleBuild.builderId.bucket, 'prod');
- expect(scheduleBuild.builderId.builder, 'Linux 1');
- expect(scheduleBuild.notify?.pubsubTopic, 'projects/flutter-dashboard/topics/luci-builds-prod');
- final Map<String, dynamic> userData =
- jsonDecode(String.fromCharCodes(base64Decode(scheduleBuild.notify!.userData!))) as Map<String, dynamic>;
- expect(userData, <String, dynamic>{
- 'commit_key': 'flutter/flutter/master/1',
- 'task_key': '1',
- 'check_run_id': 1,
- 'commit_sha': '0',
- 'commit_branch': 'master',
- 'builder_name': 'Linux 1',
- 'repo_owner': 'flutter',
- 'repo_name': 'packages',
- });
- });
-
- test('return the orignal list when hitting buildbucket exception', () async {
- final Commit commit = generateCommit(0, repo: 'packages');
- when(mockBuildBucketClient.listBuilders(any)).thenAnswer((_) async {
- throw const BuildBucketException(1, 'error');
- });
- final Tuple<Target, Task, int> toBeScheduled = Tuple<Target, Task, int>(
- generateTarget(
- 1,
- properties: <String, String>{
- 'os': 'debian-10.12',
- },
- slug: RepositorySlug('flutter', 'packages'),
- ),
- generateTask(1),
- LuciBuildService.kDefaultPriority,
- );
- final List<Tuple<Target, Task, int>> results = await service.schedulePostsubmitBuilds(
- commit: commit,
- toBeScheduled: <Tuple<Target, Task, int>>[
- toBeScheduled,
- ],
- );
- expect(results, <Tuple<Target, Task, int>>[
- toBeScheduled,
- ]);
- });
-
- test('reschedule using checkrun event fails gracefully', () async {
- when(mockGithubChecksUtil.createCheckRun(any, any, any, any))
- .thenAnswer((_) async => generateCheckRun(1, name: 'Linux 1'));
-
- when(mockBuildBucketClient.batch(any)).thenAnswer((_) async {
- return const BatchResponse(
- responses: <Response>[
- Response(
- searchBuilds: SearchBuildsResponse(
- builds: <Build>[],
- ),
- ),
- ],
- );
- });
-
- final pushMessage = generateCheckRunEvent(action: 'created', numberOfPullRequests: 1);
- final Map<String, dynamic> jsonMap = json.decode(pushMessage.data!);
- final Map<String, dynamic> jsonSubMap = json.decode(jsonMap['2']);
- final cocoon_checks.CheckRunEvent checkRunEvent = cocoon_checks.CheckRunEvent.fromJson(jsonSubMap);
-
- expect(
- () async => service.reschedulePostsubmitBuildUsingCheckRunEvent(
- checkRunEvent,
- commit: generateCommit(0),
- task: generateTask(0),
- target: generateTarget(0),
- ),
- throwsA(const TypeMatcher<NoBuildFoundException>()),
- );
- });
-
- test('do not create postsubmit checkrun for bringup: true target', () async {
- when(mockGithubChecksUtil.createCheckRun(any, any, any, any))
- .thenAnswer((_) async => generateCheckRun(1, name: 'Linux 1'));
- final Commit commit = generateCommit(0, repo: Config.packagesSlug.name);
- when(mockBuildBucketClient.listBuilders(any)).thenAnswer((_) async {
- return const ListBuildersResponse(
- builders: [
- BuilderItem(id: BuilderId(bucket: 'prod', project: 'flutter', builder: 'Linux 1')),
- ],
- );
- });
- final Tuple<Target, Task, int> toBeScheduled = Tuple<Target, Task, int>(
- generateTarget(
- 1,
- properties: <String, String>{
- 'os': 'debian-10.12',
- },
- bringup: true,
- slug: Config.packagesSlug,
- ),
- generateTask(1, parent: commit),
- LuciBuildService.kDefaultPriority,
- );
- await service.schedulePostsubmitBuilds(
- commit: commit,
- toBeScheduled: <Tuple<Target, Task, int>>[
- toBeScheduled,
- ],
- );
- // Only one batch request should be published
- expect(pubsub.messages.length, 1);
- final BatchRequest request = pubsub.messages.first as BatchRequest;
- expect(request.requests?.single.scheduleBuild, isNotNull);
- final ScheduleBuildRequest scheduleBuild = request.requests!.single.scheduleBuild!;
- expect(scheduleBuild.builderId.bucket, 'staging');
- expect(scheduleBuild.builderId.builder, 'Linux 1');
- expect(scheduleBuild.notify?.pubsubTopic, 'projects/flutter-dashboard/topics/luci-builds-prod');
- final Map<String, dynamic> userData =
- jsonDecode(String.fromCharCodes(base64Decode(scheduleBuild.notify!.userData!))) as Map<String, dynamic>;
- // No check run related data.
- expect(userData, <String, dynamic>{
- 'commit_key': 'flutter/packages/master/0',
- 'task_key': '1',
- });
- });
-
- test('Skip non-existing builder', () async {
- when(mockGithubChecksUtil.createCheckRun(any, any, any, any))
- .thenAnswer((_) async => generateCheckRun(1, name: 'Linux 1'));
- final Commit commit = generateCommit(0);
- when(mockGithubChecksUtil.createCheckRun(any, any, any, any))
- .thenAnswer((_) async => generateCheckRun(1, name: 'Linux 2'));
- when(mockBuildBucketClient.listBuilders(any)).thenAnswer((_) async {
- return const ListBuildersResponse(
- builders: [
- BuilderItem(id: BuilderId(bucket: 'prod', project: 'flutter', builder: 'Linux 2')),
- ],
- );
- });
- final Tuple<Target, Task, int> toBeScheduled1 = Tuple<Target, Task, int>(
- generateTarget(
- 1,
- properties: <String, String>{
- 'os': 'debian-10.12',
- },
- ),
- generateTask(1),
- LuciBuildService.kDefaultPriority,
- );
- final Tuple<Target, Task, int> toBeScheduled2 = Tuple<Target, Task, int>(
- generateTarget(
- 2,
- properties: <String, String>{
- 'os': 'debian-10.12',
- },
- ),
- generateTask(1),
- LuciBuildService.kDefaultPriority,
- );
- await service.schedulePostsubmitBuilds(
- commit: commit,
- toBeScheduled: <Tuple<Target, Task, int>>[
- toBeScheduled1,
- toBeScheduled2,
- ],
- );
- expect(pubsub.messages.length, 1);
- final BatchRequest request = pubsub.messages.first as BatchRequest;
- // Only existing builder: `Linux 2` is scheduled.
- expect(request.requests?.length, 1);
- expect(request.requests?.single.scheduleBuild, isNotNull);
- final ScheduleBuildRequest scheduleBuild = request.requests!.single.scheduleBuild!;
- expect(scheduleBuild.builderId.bucket, 'prod');
- expect(scheduleBuild.builderId.builder, 'Linux 2');
- });
- });
-
- group('schedulePresubmitBuilds', () {
- setUp(() {
- cache = CacheService(inMemory: true);
- mockBuildBucketClient = MockBuildBucketClient();
- pubsub = FakePubSub();
- service = LuciBuildService(
- config: FakeConfig(),
- cache: cache,
- buildBucketClient: mockBuildBucketClient,
- githubChecksUtil: mockGithubChecksUtil,
- pubsub: pubsub,
- );
- });
- test('reschedule using checkrun event', () async {
- when(mockGithubChecksUtil.createCheckRun(any, any, any, any))
- .thenAnswer((_) async => generateCheckRun(1, name: 'Linux 1'));
-
- when(mockBuildBucketClient.batch(any)).thenAnswer((_) async {
- return BatchResponse(
- responses: <Response>[
- Response(
- searchBuilds: SearchBuildsResponse(
- builds: <Build>[
- generateBuild(
- 1,
- name: 'Linux',
- status: Status.ended,
- tags: {
- 'buildset': <String>['pr/git/123'],
- 'cipd_version': <String>['refs/heads/main'],
- 'github_link': <String>['https://github.com/flutter/flutter/pull/1'],
- },
- input: const Input(properties: {'test': 'abc'}),
- ),
- ],
- ),
- ),
- ],
- );
- });
- when(mockBuildBucketClient.scheduleBuild(any)).thenAnswer((_) async => generateBuild(1));
-
- final pushMessage = generateCheckRunEvent(action: 'created', numberOfPullRequests: 1);
- final Map<String, dynamic> jsonMap = json.decode(pushMessage.data!);
- final Map<String, dynamic> jsonSubMap = json.decode(jsonMap['2']);
- final cocoon_checks.CheckRunEvent checkRunEvent = cocoon_checks.CheckRunEvent.fromJson(jsonSubMap);
-
- await service.reschedulePresubmitBuildUsingCheckRunEvent(
- checkRunEvent,
- );
- final List<dynamic> captured = verify(
- mockBuildBucketClient.scheduleBuild(
- captureAny,
- ),
- ).captured;
- expect(captured.length, 1);
- final ScheduleBuildRequest scheduleBuildRequest = captured[0] as ScheduleBuildRequest;
- final Map<String, dynamic> userData =
- jsonDecode(String.fromCharCodes(base64Decode(scheduleBuildRequest.notify!.userData!)))
- as Map<String, dynamic>;
- expect(userData, <String, dynamic>{
- 'check_run_id': 1,
- 'commit_branch': 'master',
- 'commit_sha': 'ec26c3e57ca3a959ca5aad62de7213c562f8c821',
- 'repo_owner': 'flutter',
- 'repo_name': 'flutter',
- 'user_agent': 'flutter-cocoon',
- });
- });
- });
-
- group('cancelBuilds', () {
- setUp(() {
- cache = CacheService(inMemory: true);
- config = FakeConfig();
- mockBuildBucketClient = MockBuildBucketClient();
- pubsub = FakePubSub();
- service = LuciBuildService(
- config: config,
- cache: cache,
- buildBucketClient: mockBuildBucketClient,
- pubsub: pubsub,
- );
- slug = RepositorySlug('flutter', 'cocoon');
- });
-
- test('Cancel builds when build list is empty', () async {
- when(mockBuildBucketClient.batch(any)).thenAnswer((_) async {
- return const BatchResponse(
- responses: <Response>[],
- );
- });
- await service.cancelBuilds(pullRequest, 'new builds');
- // This is okay, it is getting called twice when it runs cancel builds
- // because the call is no longer being short-circuited. It calls batch in
- // tryBuildsForPullRequest and it calls in the top level cancelBuilds
- // function.
- verify(mockBuildBucketClient.batch(any)).called(1);
- });
-
- test('Cancel builds that are scheduled', () async {
- when(mockBuildBucketClient.batch(any)).thenAnswer((_) async {
- return BatchResponse(
- responses: <Response>[
- Response(
- searchBuilds: SearchBuildsResponse(
- builds: <Build>[
- generateBuild(998, name: 'Linux', status: Status.started),
- ],
- ),
- ),
- ],
- );
- });
- await service.cancelBuilds(pullRequest, 'new builds');
- expect(
- verify(mockBuildBucketClient.batch(captureAny)).captured[1].requests[0].cancelBuild.toJson(),
- json.decode('{"id": "998", "summaryMarkdown": "new builds"}'),
- );
- });
- });
-
- group('failedBuilds', () {
- setUp(() {
- cache = CacheService(inMemory: true);
- githubService = FakeGithubService();
- config = FakeConfig(githubService: githubService);
- mockBuildBucketClient = MockBuildBucketClient();
- pubsub = FakePubSub();
- service = LuciBuildService(
- config: config,
- cache: cache,
- buildBucketClient: mockBuildBucketClient,
- pubsub: pubsub,
- );
- slug = RepositorySlug('flutter', 'flutter');
- });
-
- test('Failed builds from an empty list', () async {
- when(mockBuildBucketClient.batch(any)).thenAnswer((_) async {
- return const BatchResponse(
- responses: <Response>[],
- );
- });
- final List<Build?> result = await service.failedBuilds(pullRequest, <Target>[]);
- expect(result, isEmpty);
- });
-
- test('Failed builds from a list of builds with failures', () async {
- when(mockBuildBucketClient.batch(any)).thenAnswer((_) async {
- return BatchResponse(
- responses: <Response>[
- Response(
- searchBuilds: SearchBuildsResponse(
- builds: <Build>[
- generateBuild(998, name: 'Linux 1', status: Status.failure),
- ],
- ),
- ),
- ],
- );
- });
- final List<Build?> result = await service.failedBuilds(pullRequest, <Target>[generateTarget(1)]);
- expect(result, hasLength(1));
- });
- });
-
- group('rescheduleBuild', () {
- late push_message.BuildPushMessage buildPushMessage;
-
- setUp(() {
- cache = CacheService(inMemory: true);
- config = FakeConfig();
- mockBuildBucketClient = MockBuildBucketClient();
- pubsub = FakePubSub();
- service = LuciBuildService(
- config: config,
- cache: cache,
- buildBucketClient: mockBuildBucketClient,
- pubsub: pubsub,
- );
- final Map<String, dynamic> json = jsonDecode(
- buildPushMessageString(
- 'COMPLETED',
- result: 'FAILURE',
- builderName: 'Linux Host Engine',
- userData: '{}',
- ),
- ) as Map<String, dynamic>;
- buildPushMessage = push_message.BuildPushMessage.fromJson(json);
- });
-
- test('Reschedule an existing build', () async {
- when(mockBuildBucketClient.scheduleBuild(any)).thenAnswer((_) async => generateBuild(1));
- final build = await service.rescheduleBuild(
- builderName: 'mybuild',
- buildPushMessage: buildPushMessage,
- rescheduleAttempt: 2,
- );
- expect(build.id, '1');
- expect(build.status, Status.success);
- final List<dynamic> captured = verify(mockBuildBucketClient.scheduleBuild(captureAny)).captured;
- expect(captured.length, 1);
- final ScheduleBuildRequest scheduleBuildRequest = captured[0] as ScheduleBuildRequest;
- // This is to validate `scheduleBuildRequest` can be json.encoded correctly.
- // It complains when some non-String typed data exists.
- expect(json.encode(scheduleBuildRequest), isNotNull);
- expect(scheduleBuildRequest.tags!.containsKey('current_attempt'), true);
- expect(scheduleBuildRequest.tags!['current_attempt'], <String>['2']);
- });
- });
-
- group('checkRerunBuilder', () {
- late Commit commit;
- late Commit totCommit;
- late DatastoreService datastore;
- late MockGithubChecksUtil mockGithubChecksUtil;
- setUp(() {
- cache = CacheService(inMemory: true);
- config = FakeConfig();
- mockBuildBucketClient = MockBuildBucketClient();
- mockGithubChecksUtil = MockGithubChecksUtil();
- when(mockGithubChecksUtil.createCheckRun(any, any, any, any, output: anyNamed('output')))
- .thenAnswer((realInvocation) async => generateCheckRun(1));
- pubsub = FakePubSub();
- service = LuciBuildService(
- config: config,
- cache: cache,
- buildBucketClient: mockBuildBucketClient,
- githubChecksUtil: mockGithubChecksUtil,
- pubsub: pubsub,
- );
- datastore = DatastoreService(config.db, 5);
- });
-
- test('Pass repo and properties correctly', () async {
- totCommit = generateCommit(1, repo: 'engine', branch: 'main');
- config.db.values[totCommit.key] = totCommit;
- config.maxLuciTaskRetriesValue = 1;
- final Task task = generateTask(
- 1,
- status: Task.statusFailed,
- parent: totCommit,
- buildNumber: 1,
- );
- final Target target = generateTarget(1);
- expect(task.attempts, 1);
- expect(task.status, Task.statusFailed);
- final bool rerunFlag = await service.checkRerunBuilder(
- commit: totCommit,
- task: task,
- target: target,
- datastore: datastore,
- );
- expect(pubsub.messages.length, 1);
- final ScheduleBuildRequest scheduleBuildRequest =
- (pubsub.messages.single as BatchRequest).requests!.single.scheduleBuild!;
- final Map<String, dynamic> properties = scheduleBuildRequest.properties!;
- for (String key in Config.defaultProperties.keys) {
- expect(properties.containsKey(key), true);
- }
- expect(scheduleBuildRequest.priority, LuciBuildService.kRerunPriority);
- expect(scheduleBuildRequest.gitilesCommit?.project, 'mirrors/engine');
- expect(rerunFlag, isTrue);
- expect(task.attempts, 2);
- expect(task.status, Task.statusInProgress);
- });
-
- test('Rerun a test failed builder', () async {
- totCommit = generateCommit(1);
- config.db.values[totCommit.key] = totCommit;
- config.maxLuciTaskRetriesValue = 1;
- final Task task = generateTask(
- 1,
- status: Task.statusFailed,
- parent: totCommit,
- buildNumber: 1,
- );
- final Target target = generateTarget(1);
- final bool rerunFlag = await service.checkRerunBuilder(
- commit: totCommit,
- task: task,
- target: target,
- datastore: datastore,
- );
- expect(rerunFlag, isTrue);
- });
-
- test('Rerun an infra failed builder', () async {
- totCommit = generateCommit(1);
- config.db.values[totCommit.key] = totCommit;
- config.maxLuciTaskRetriesValue = 1;
- final Task task = generateTask(
- 1,
- status: Task.statusInfraFailure,
- parent: totCommit,
- buildNumber: 1,
- );
- final Target target = generateTarget(1);
- final bool rerunFlag = await service.checkRerunBuilder(
- commit: totCommit,
- task: task,
- target: target,
- datastore: datastore,
- );
- expect(rerunFlag, isTrue);
- });
-
- test('Do not rerun a successful builder', () async {
- totCommit = generateCommit(1);
- config.db.values[totCommit.key] = totCommit;
- config.maxLuciTaskRetriesValue = 1;
- final Task task = generateTask(
- 1,
- status: Task.statusSucceeded,
- parent: totCommit,
- buildNumber: 1,
- );
- final Target target = generateTarget(1);
- final bool rerunFlag = await service.checkRerunBuilder(
- commit: totCommit,
- task: task,
- target: target,
- datastore: datastore,
- );
- expect(rerunFlag, isFalse);
- });
-
- test('Do not rerun a builder exceeding retry limit', () async {
- totCommit = generateCommit(1);
- config.db.values[totCommit.key] = totCommit;
- config.maxLuciTaskRetriesValue = 1;
- final Task task = generateTask(
- 1,
- status: Task.statusInfraFailure,
- parent: totCommit,
- buildNumber: 1,
- attempts: 2,
- );
- final Target target = generateTarget(1);
- final bool rerunFlag = await service.checkRerunBuilder(
- commit: totCommit,
- task: task,
- target: target,
- datastore: datastore,
- );
- expect(rerunFlag, isFalse);
- });
-
- test('Do not rerun a builder when not tip of tree', () async {
- totCommit = generateCommit(2, sha: 'def');
- commit = generateCommit(1, sha: 'abc');
- config.db.values[totCommit.key] = totCommit;
- config.db.values[commit.key] = commit;
- config.maxLuciTaskRetriesValue = 1;
- final Task task = generateTask(
- 1,
- status: Task.statusInfraFailure,
- parent: commit,
- buildNumber: 1,
- );
- final Target target = generateTarget(1);
- final bool rerunFlag = await service.checkRerunBuilder(
- commit: commit,
- task: task,
- target: target,
- datastore: datastore,
- );
- expect(rerunFlag, isFalse);
- });
- });
-}
diff --git a/app_dart/test/service/scheduler/graph_test.dart b/app_dart/test/service/scheduler/graph_test.dart
deleted file mode 100644
index 2c3b96a..0000000
--- a/app_dart/test/service/scheduler/graph_test.dart
+++ /dev/null
@@ -1,372 +0,0 @@
-// Copyright 2021 The Flutter 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 'package:cocoon_service/src/model/ci_yaml/ci_yaml.dart';
-import 'package:cocoon_service/src/model/proto/internal/scheduler.pb.dart';
-import 'package:cocoon_service/src/service/config.dart';
-
-import 'package:test/test.dart';
-import 'package:yaml/yaml.dart';
-
-void main() {
- group('scheduler config', () {
- test('constructs graph with one target', () {
- final YamlMap? singleTargetConfig = loadYaml('''
-enabled_branches:
- - master
-targets:
- - name: A
- builder: builderA
- drone_dimensions:
- - os=Linux
- properties:
- test: abc
- ''') as YamlMap?;
- final SchedulerConfig unCheckedSchedulerConfig = SchedulerConfig()..mergeFromProto3Json(singleTargetConfig);
- final SchedulerConfig schedulerConfig = CiYaml(
- slug: Config.flutterSlug,
- branch: Config.defaultBranch(Config.flutterSlug),
- config: unCheckedSchedulerConfig,
- validate: true,
- ).config;
- expect(schedulerConfig.enabledBranches, <String>['master']);
- expect(schedulerConfig.targets.length, 1);
- final Target target = schedulerConfig.targets.first;
- expect(target.bringup, false);
- expect(target.name, 'A');
- expect(target.properties, <String, String>{
- 'test': 'abc',
- });
- expect(target.scheduler, SchedulerSystem.cocoon);
- expect(target.testbed, 'linux-vm');
- expect(target.timeout, 30);
- expect(target.droneDimensions, ['os=Linux']);
- });
-
- test('throws exception when non-existent scheduler is given', () {
- final YamlMap? targetWithNonexistentScheduler = loadYaml('''
-enabled_branches:
- - master
-targets:
- - name: A
- scheduler: dashatar
- ''') as YamlMap?;
- expect(
- () {
- final SchedulerConfig unCheckedSchedulerConfig = SchedulerConfig()
- ..mergeFromProto3Json(targetWithNonexistentScheduler);
- CiYaml(
- slug: Config.flutterSlug,
- branch: Config.defaultBranch(Config.flutterSlug),
- config: unCheckedSchedulerConfig,
- validate: true,
- ).config;
- },
- throwsA(isA<FormatException>()),
- );
- });
-
- test('constructs graph with dependency chain', () {
- final YamlMap? dependentTargetConfig = loadYaml('''
-enabled_branches:
- - master
-targets:
- - name: A
- - name: B
- dependencies:
- - A
- properties:
- dependencies: >-
- [
- {"dependency": "android_sdk", "version": "version:31v8"},
- {"dependency": "certs", "version": "version:9563bb"},
- {"dependency": "chrome_and_driver", "version": "version:96.2"},
- {"dependency": "open_jdk", "version": "11"}
- ]
- - name: C
- dependencies:
- - B
- ''') as YamlMap?;
- final SchedulerConfig unCheckedSchedulerConfig = SchedulerConfig()..mergeFromProto3Json(dependentTargetConfig);
- final SchedulerConfig schedulerConfig = CiYaml(
- slug: Config.flutterSlug,
- branch: Config.defaultBranch(Config.flutterSlug),
- config: unCheckedSchedulerConfig,
- validate: true,
- ).config;
- expect(schedulerConfig.targets.length, 3);
- final Target a = schedulerConfig.targets.first;
- final Target b = schedulerConfig.targets[1];
- final Target c = schedulerConfig.targets[2];
- expect(a.name, 'A');
- expect(b.name, 'B');
- expect(b.dependencies, <String>['A']);
- expect(c.name, 'C');
- expect(c.dependencies, <String>['B']);
- });
-
- test('constructs graph with parent with two dependents', () {
- final YamlMap? twoDependentTargetConfig = loadYaml('''
-enabled_branches:
- - master
-targets:
- - name: A
- - name: B1
- dependencies:
- - A
- - name: B2
- dependencies:
- - A
- ''') as YamlMap?;
- final SchedulerConfig unCheckedSchedulerConfig = SchedulerConfig()..mergeFromProto3Json(twoDependentTargetConfig);
- final SchedulerConfig schedulerConfig = CiYaml(
- slug: Config.flutterSlug,
- branch: Config.defaultBranch(Config.flutterSlug),
- config: unCheckedSchedulerConfig,
- validate: true,
- ).config;
- expect(schedulerConfig.targets.length, 3);
- final Target a = schedulerConfig.targets.first;
- final Target b1 = schedulerConfig.targets[1];
- final Target b2 = schedulerConfig.targets[2];
- expect(a.name, 'A');
- expect(b1.name, 'B1');
- expect(b1.dependencies, <String>['A']);
- expect(b2.name, 'B2');
- expect(b2.dependencies, <String>['A']);
- });
-
- test('fails when there are cyclic targets', () {
- final YamlMap? configWithCycle = loadYaml('''
-enabled_branches:
- - master
-targets:
- - name: A
- dependencies:
- - B
- - name: B
- dependencies:
- - A
- ''') as YamlMap?;
- final SchedulerConfig unCheckedSchedulerConfig = SchedulerConfig()..mergeFromProto3Json(configWithCycle);
- expect(
- () => CiYaml(
- slug: Config.flutterSlug,
- branch: Config.defaultBranch(Config.flutterSlug),
- config: unCheckedSchedulerConfig,
- validate: true,
- ).config,
- throwsA(
- isA<FormatException>().having(
- (FormatException e) => e.toString(),
- 'message',
- contains('ERROR: A depends on B which does not exist'),
- ),
- ),
- );
- });
-
- test('fails when there are duplicate targets', () {
- final YamlMap? configWithDuplicateTargets = loadYaml('''
-enabled_branches:
- - master
-targets:
- - name: A
- - name: A
- ''') as YamlMap?;
- final SchedulerConfig unCheckedSchedulerConfig = SchedulerConfig()
- ..mergeFromProto3Json(configWithDuplicateTargets);
- expect(
- () => CiYaml(
- slug: Config.flutterSlug,
- branch: Config.defaultBranch(Config.flutterSlug),
- config: unCheckedSchedulerConfig,
- validate: true,
- ).config,
- throwsA(
- isA<FormatException>().having(
- (FormatException e) => e.toString(),
- 'message',
- contains('ERROR: A already exists in graph'),
- ),
- ),
- );
- });
-
- test('fails when there are multiple dependencies', () {
- final YamlMap? configWithMultipleDependencies = loadYaml('''
-enabled_branches:
- - master
-targets:
- - name: A
- - name: B
- - name: C
- dependencies:
- - A
- - B
- ''') as YamlMap?;
- final SchedulerConfig unCheckedSchedulerConfig = SchedulerConfig()
- ..mergeFromProto3Json(configWithMultipleDependencies);
- expect(
- () => CiYaml(
- slug: Config.flutterSlug,
- branch: Config.defaultBranch(Config.flutterSlug),
- config: unCheckedSchedulerConfig,
- validate: true,
- ).config,
- throwsA(
- isA<FormatException>().having(
- (FormatException e) => e.toString(),
- 'message',
- contains('ERROR: C has multiple dependencies which is not supported. Use only one dependency'),
- ),
- ),
- );
- });
-
- test('fails when dependency does not exist', () {
- final YamlMap? configWithMissingTarget = loadYaml('''
-enabled_branches:
- - master
-targets:
- - name: A
- dependencies:
- - B
- ''') as YamlMap?;
- final SchedulerConfig unCheckedSchedulerConfig = SchedulerConfig()..mergeFromProto3Json(configWithMissingTarget);
- expect(
- () => CiYaml(
- slug: Config.flutterSlug,
- branch: Config.defaultBranch(Config.flutterSlug),
- config: unCheckedSchedulerConfig,
- validate: true,
- ).config,
- throwsA(
- isA<FormatException>().having(
- (FormatException e) => e.toString(),
- 'message',
- contains('ERROR: A depends on B which does not exist'),
- ),
- ),
- );
- });
- });
-
- group('validate scheduler config and compared with tip of tree targets', () {
- late CiYaml? totConfig;
-
- setUp(() {
- final YamlMap? totYaml = loadYaml('''
-enabled_branches:
- - master
-targets:
- - name: A
- ''') as YamlMap?;
- final SchedulerConfig unCheckedSchedulerConfig = SchedulerConfig()..mergeFromProto3Json(totYaml);
- totConfig = CiYaml(
- slug: Config.flutterSlug,
- branch: Config.defaultBranch(Config.flutterSlug),
- config: unCheckedSchedulerConfig,
- validate: true,
- );
- });
-
- test('succeed when no new builders compared with tip of tree builders', () {
- final YamlMap? currentYaml = loadYaml('''
-enabled_branches:
- - master
-targets:
- - name: A
- ''') as YamlMap?;
- final SchedulerConfig unCheckedSchedulerConfig = SchedulerConfig()..mergeFromProto3Json(currentYaml);
- expect(
- () => CiYaml(
- slug: Config.flutterSlug,
- branch: Config.defaultBranch(Config.flutterSlug),
- config: unCheckedSchedulerConfig,
- totConfig: totConfig,
- validate: true,
- ),
- returnsNormally,
- );
- });
-
- test('succeed when new builder is marked with bringup:true ', () {
- final YamlMap? currentYaml = loadYaml('''
-enabled_branches:
- - master
-targets:
- - name: A
- - name: B
- bringup: true
- ''') as YamlMap?;
- final SchedulerConfig unCheckedSchedulerConfig = SchedulerConfig()..mergeFromProto3Json(currentYaml);
- expect(
- () => CiYaml(
- slug: Config.flutterSlug,
- branch: Config.defaultBranch(Config.flutterSlug),
- config: unCheckedSchedulerConfig,
- totConfig: totConfig,
- validate: true,
- ),
- returnsNormally,
- );
- });
-
- test('fails when new builder is missing bringup:true ', () {
- final YamlMap? currentYaml = loadYaml('''
-enabled_branches:
- - master
-targets:
- - name: A
- - name: B
- ''') as YamlMap?;
- final SchedulerConfig unCheckedSchedulerConfig = SchedulerConfig()..mergeFromProto3Json(currentYaml);
- expect(
- () => CiYaml(
- slug: Config.flutterSlug,
- branch: Config.defaultBranch(Config.flutterSlug),
- config: unCheckedSchedulerConfig,
- totConfig: totConfig,
- validate: true,
- ),
- throwsA(
- isA<FormatException>().having(
- (FormatException e) => e.toString(),
- 'message',
- contains('ERROR: B is a new builder added. it needs to be marked bringup: true'),
- ),
- ),
- );
- });
-
- test('fails when new builder has bringup set to false ', () {
- final YamlMap? currentYaml = loadYaml('''
-enabled_branches:
- - master
-targets:
- - name: A
- - name: B
- bringup: false
- ''') as YamlMap?;
- final SchedulerConfig unCheckedSchedulerConfig = SchedulerConfig()..mergeFromProto3Json(currentYaml);
- expect(
- () => CiYaml(
- slug: Config.flutterSlug,
- branch: Config.defaultBranch(Config.flutterSlug),
- config: unCheckedSchedulerConfig,
- totConfig: totConfig,
- validate: true,
- ),
- throwsA(
- isA<FormatException>().having(
- (FormatException e) => e.toString(),
- 'message',
- contains('ERROR: B is a new builder added. it needs to be marked bringup: true'),
- ),
- ),
- );
- });
- });
-}
diff --git a/app_dart/test/service/scheduler/policy_test.dart b/app_dart/test/service/scheduler/policy_test.dart
deleted file mode 100644
index 4400640..0000000
--- a/app_dart/test/service/scheduler/policy_test.dart
+++ /dev/null
@@ -1,157 +0,0 @@
-// Copyright 2021 The Flutter 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 'package:cocoon_service/src/model/appengine/task.dart';
-import 'package:cocoon_service/src/service/datastore.dart';
-import 'package:cocoon_service/src/service/luci_build_service.dart';
-import 'package:cocoon_service/src/service/scheduler/policy.dart';
-import 'package:test/test.dart';
-
-import '../../src/datastore/fake_datastore.dart';
-import '../../src/utilities/entity_generators.dart';
-
-void main() {
- group('BatchPolicy', () {
- late FakeDatastoreDB db;
- late DatastoreService datastore;
-
- final BatchPolicy policy = BatchPolicy();
-
- setUp(() {
- db = FakeDatastoreDB();
- datastore = DatastoreService(db, 5);
- });
-
- final List<Task> allPending = <Task>[
- generateTask(3),
- generateTask(2),
- generateTask(1),
- ];
-
- final List<Task> latestAllPending = <Task>[
- generateTask(6),
- generateTask(5),
- generateTask(4),
- generateTask(3),
- generateTask(2),
- generateTask(1, status: Task.statusSucceeded),
- ];
-
- final List<Task> latestFinishedButRestPending = <Task>[
- generateTask(6, status: Task.statusSucceeded),
- generateTask(5),
- generateTask(4),
- generateTask(3),
- generateTask(2),
- generateTask(1),
- ];
-
- final List<Task> latestFailed = <Task>[
- generateTask(6, status: Task.statusFailed),
- generateTask(5),
- generateTask(4),
- generateTask(3),
- generateTask(2),
- generateTask(1),
- ];
-
- final List<Task> latestPending = <Task>[
- generateTask(6),
- generateTask(5),
- generateTask(4),
- generateTask(3),
- generateTask(2, status: Task.statusSucceeded),
- generateTask(1, status: Task.statusSucceeded),
- ];
-
- final List<Task> failedWithRunning = <Task>[
- generateTask(6),
- generateTask(5),
- generateTask(4),
- generateTask(3, status: Task.statusFailed),
- generateTask(2, status: Task.statusInProgress),
- generateTask(1),
- ];
-
- test('triggers if less tasks than batch size', () async {
- db.addOnQuery<Task>((Iterable<Task> results) => allPending);
- expect(
- await policy.triggerPriority(task: generateTask(4), datastore: datastore),
- null,
- );
- });
-
- test('triggers after batch size', () async {
- db.addOnQuery<Task>((Iterable<Task> results) => latestAllPending);
- expect(
- await policy.triggerPriority(task: generateTask(7), datastore: datastore),
- LuciBuildService.kDefaultPriority,
- );
- });
-
- test('triggers with higher priority on recent failures', () async {
- db.addOnQuery<Task>((Iterable<Task> results) => latestFailed);
- expect(
- await policy.triggerPriority(task: generateTask(7), datastore: datastore),
- LuciBuildService.kRerunPriority,
- );
- });
-
- test('does not trigger on recent failures if there is already a running task', () async {
- db.addOnQuery<Task>((Iterable<Task> results) => failedWithRunning);
- expect(
- await policy.triggerPriority(task: generateTask(7), datastore: datastore),
- isNull,
- );
- });
-
- test('does not trigger when a test was recently scheduled', () async {
- db.addOnQuery<Task>((Iterable<Task> results) => latestFinishedButRestPending);
- expect(await policy.triggerPriority(task: generateTask(7), datastore: datastore), isNull);
- });
-
- test('does not trigger when pending queue is smaller than batch', () async {
- db.addOnQuery<Task>((Iterable<Task> results) => latestPending);
- expect(await policy.triggerPriority(task: generateTask(7), datastore: datastore), isNull);
- });
-
- test('do not return rerun priority when tasks length is smaller than batch size', () {
- expect(shouldRerunPriority(allPending, 5), false);
- });
- });
-
- group('GuaranteedPolicy', () {
- late FakeDatastoreDB db;
- late DatastoreService datastore;
-
- final GuaranteedPolicy policy = GuaranteedPolicy();
-
- setUp(() {
- db = FakeDatastoreDB();
- datastore = DatastoreService(db, 5);
- });
-
- final List<Task> pending = <Task>[
- generateTask(1),
- ];
-
- final List<Task> latestFailed = <Task>[generateTask(1, status: Task.statusFailed)];
-
- test('triggers every task', () async {
- db.addOnQuery<Task>((Iterable<Task> results) => pending);
- expect(
- await policy.triggerPriority(task: generateTask(2), datastore: datastore),
- LuciBuildService.kDefaultPriority,
- );
- });
-
- test('triggers with higher priority on recent failure', () async {
- db.addOnQuery<Task>((Iterable<Task> results) => latestFailed);
- expect(
- await policy.triggerPriority(task: generateTask(2), datastore: datastore),
- LuciBuildService.kRerunPriority,
- );
- });
- });
-}
diff --git a/app_dart/test/service/scheduler_test.dart b/app_dart/test/service/scheduler_test.dart
deleted file mode 100644
index 0d94266..0000000
--- a/app_dart/test/service/scheduler_test.dart
+++ /dev/null
@@ -1,1181 +0,0 @@
-// Copyright 2021 The Flutter 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:convert';
-import 'dart:io';
-
-import 'package:cocoon_service/src/foundation/utils.dart';
-import 'package:cocoon_service/src/model/appengine/commit.dart';
-import 'package:cocoon_service/src/model/appengine/stage.dart';
-import 'package:cocoon_service/src/model/appengine/task.dart';
-import 'package:cocoon_service/src/model/ci_yaml/target.dart';
-import 'package:cocoon_service/src/model/github/checks.dart' as cocoon_checks;
-import 'package:cocoon_service/src/model/luci/buildbucket.dart';
-import 'package:cocoon_service/src/service/build_status_provider.dart';
-import 'package:cocoon_service/src/service/cache_service.dart';
-import 'package:cocoon_service/src/service/config.dart';
-import 'package:cocoon_service/src/service/datastore.dart';
-import 'package:cocoon_service/src/service/github_checks_service.dart';
-import 'package:cocoon_service/src/service/scheduler.dart';
-import 'package:gcloud/db.dart' as gcloud_db;
-import 'package:gcloud/db.dart';
-import 'package:github/github.dart';
-import 'package:github/hooks.dart';
-import 'package:googleapis/bigquery/v2.dart';
-import 'package:http/http.dart' as http;
-import 'package:http/testing.dart';
-import 'package:mockito/mockito.dart';
-import 'package:test/test.dart';
-
-import '../model/github/checks_test_data.dart';
-import '../src/datastore/fake_config.dart';
-import '../src/datastore/fake_datastore.dart';
-import '../src/service/fake_build_status_provider.dart';
-import '../src/request_handling/fake_pubsub.dart';
-import '../src/service/fake_buildbucket.dart';
-import '../src/service/fake_gerrit_service.dart';
-import '../src/service/fake_github_service.dart';
-import '../src/service/fake_luci_build_service.dart';
-import '../src/utilities/entity_generators.dart';
-import '../src/utilities/mocks.dart';
-
-const String singleCiYaml = r'''
-enabled_branches:
- - master
- - main
- - flutter-\d+\.\d+-candidate\.\d+
-targets:
- - name: Linux A
- properties:
- custom: abc
- - name: Linux B
- enabled_branches:
- - stable
- scheduler: luci
- - name: Linux runIf
- runIf:
- - dev/**
- - name: Google Internal Roll
- postsubmit: true
- presubmit: false
- scheduler: google_internal
-''';
-
-void main() {
- late CacheService cache;
- late FakeConfig config;
- late FakeDatastoreDB db;
- late FakeBuildStatusService buildStatusService;
- late MockClient httpClient;
- late MockGithubChecksUtil mockGithubChecksUtil;
- late Scheduler scheduler;
-
- final PullRequest pullRequest = generatePullRequest(id: 42);
-
- Commit shaToCommit(String sha, {String branch = 'master'}) {
- return Commit(
- key: db.emptyKey.append(Commit, id: 'flutter/flutter/$branch/$sha'),
- repository: 'flutter/flutter',
- sha: sha,
- branch: branch,
- timestamp: int.parse(sha),
- );
- }
-
- group('Scheduler', () {
- setUp(() {
- final MockTabledataResource tabledataResource = MockTabledataResource();
- when(tabledataResource.insertAll(any, any, any, any)).thenAnswer((_) async {
- return TableDataInsertAllResponse();
- });
-
- cache = CacheService(inMemory: true);
- db = FakeDatastoreDB();
- buildStatusService = FakeBuildStatusService(
- commitStatuses: <CommitStatus>[
- CommitStatus(generateCommit(1), const <Stage>[]),
- CommitStatus(generateCommit(1, branch: 'main', repo: Config.engineSlug.name), const <Stage>[]),
- ],
- );
- config = FakeConfig(
- tabledataResource: tabledataResource,
- dbValue: db,
- githubService: FakeGithubService(),
- githubClient: MockGitHub(),
- supportedReposValue: <RepositorySlug>{
- Config.engineSlug,
- Config.flutterSlug,
- },
- );
- httpClient = MockClient((http.Request request) async {
- if (request.url.path.contains('.ci.yaml')) {
- return http.Response(singleCiYaml, 200);
- }
- throw Exception('Failed to find ${request.url.path}');
- });
-
- mockGithubChecksUtil = MockGithubChecksUtil();
- // Generate check runs based on the name hash code
- when(mockGithubChecksUtil.createCheckRun(any, any, any, any, output: anyNamed('output')))
- .thenAnswer((Invocation invocation) async => generateCheckRun(invocation.positionalArguments[2].hashCode));
- scheduler = Scheduler(
- cache: cache,
- config: config,
- datastoreProvider: (DatastoreDB db) => DatastoreService(db, 2),
- buildStatusProvider: (_) => buildStatusService,
- githubChecksService: GithubChecksService(config, githubChecksUtil: mockGithubChecksUtil),
- httpClientProvider: () => httpClient,
- luciBuildService: FakeLuciBuildService(
- config: config,
- githubChecksUtil: mockGithubChecksUtil,
- gerritService: FakeGerritService(
- branchesValue: <String>['master', 'main'],
- ),
- ),
- );
-
- when(mockGithubChecksUtil.createCheckRun(any, any, any, any)).thenAnswer((_) async {
- return CheckRun.fromJson(const <String, dynamic>{
- 'id': 1,
- 'started_at': '2020-05-10T02:49:31Z',
- 'check_suite': <String, dynamic>{'id': 2},
- });
- });
- });
-
- group('add commits', () {
- final FakePubSub pubsub = FakePubSub();
- List<Commit> createCommitList(
- List<String> shas, {
- String repo = 'flutter',
- String branch = 'master',
- }) {
- return List<Commit>.generate(
- shas.length,
- (int index) => Commit(
- author: 'Username',
- authorAvatarUrl: 'http://example.org/avatar.jpg',
- branch: branch,
- key: db.emptyKey.append(Commit, id: 'flutter/$repo/$branch/${shas[index]}'),
- message: 'commit message',
- repository: 'flutter/$repo',
- sha: shas[index],
- timestamp: DateTime.fromMillisecondsSinceEpoch(int.parse(shas[index])).millisecondsSinceEpoch,
- ),
- );
- }
-
- test('succeeds when GitHub returns no commits', () async {
- await scheduler.addCommits(<Commit>[]);
- expect(db.values, isEmpty);
- });
-
- test('inserts all relevant fields of the commit', () async {
- config.supportedBranchesValue = <String>['master'];
- expect(db.values.values.whereType<Commit>().length, 0);
- await scheduler.addCommits(createCommitList(<String>['1']));
- expect(db.values.values.whereType<Commit>().length, 1);
- final Commit commit = db.values.values.whereType<Commit>().single;
- expect(commit.repository, 'flutter/flutter');
- expect(commit.branch, 'master');
- expect(commit.sha, '1');
- expect(commit.timestamp, 1);
- expect(commit.author, 'Username');
- expect(commit.authorAvatarUrl, 'http://example.org/avatar.jpg');
- expect(commit.message, 'commit message');
- });
-
- test('skips scheduling for unsupported repos', () async {
- config.supportedBranchesValue = <String>['master'];
- await scheduler.addCommits(createCommitList(<String>['1'], repo: 'not-supported'));
- expect(db.values.values.whereType<Commit>().length, 0);
- });
-
- test('skips commits for which transaction commit fails', () async {
- config.supportedBranchesValue = <String>['master'];
-
- // Existing commits should not be duplicated.
- final Commit commit = shaToCommit('1');
- db.values[commit.key] = commit;
-
- db.onCommit = (List<gcloud_db.Model<dynamic>> inserts, List<gcloud_db.Key<dynamic>> deletes) {
- if (inserts.whereType<Commit>().where((Commit commit) => commit.sha == '3').isNotEmpty) {
- throw StateError('Commit failed');
- }
- };
- // Commits are expect from newest to oldest timestamps
- await scheduler.addCommits(createCommitList(<String>['2', '3', '4']));
- expect(db.values.values.whereType<Commit>().length, 3);
- // The 2 new commits are scheduled 3 tasks, existing commit has none.
- expect(db.values.values.whereType<Task>().length, 2 * 3);
- // Check commits were added, but 3 was not
- expect(db.values.values.whereType<Commit>().map<String>(toSha), containsAll(<String>['1', '2', '4']));
- expect(db.values.values.whereType<Commit>().map<String>(toSha), isNot(contains('3')));
- });
-
- test('skips commits for which task transaction fails', () async {
- config.supportedBranchesValue = <String>['master'];
-
- // Existing commits should not be duplicated.
- final Commit commit = shaToCommit('1');
- db.values[commit.key] = commit;
-
- db.onCommit = (List<gcloud_db.Model<dynamic>> inserts, List<gcloud_db.Key<dynamic>> deletes) {
- if (inserts.whereType<Task>().where((Task task) => task.createTimestamp == 3).isNotEmpty) {
- throw StateError('Task failed');
- }
- };
- // Commits are expect from newest to oldest timestamps
- await scheduler.addCommits(createCommitList(<String>['2', '3', '4']));
- expect(db.values.values.whereType<Commit>().length, 3);
- // The 2 new commits are scheduled 3 tasks, existing commit has none.
- expect(db.values.values.whereType<Task>().length, 2 * 3);
- // Check commits were added, but 3 was not
- expect(db.values.values.whereType<Commit>().map<String>(toSha), containsAll(<String>['1', '2', '4']));
- expect(db.values.values.whereType<Commit>().map<String>(toSha), isNot(contains('3')));
- });
-
- test('schedules cocoon based targets', () async {
- final MockLuciBuildService luciBuildService = MockLuciBuildService();
- when(
- luciBuildService.schedulePostsubmitBuilds(
- commit: anyNamed('commit'),
- toBeScheduled: captureAnyNamed('toBeScheduled'),
- ),
- ).thenAnswer((_) => Future<List<Tuple<Target, Task, int>>>.value(<Tuple<Target, Task, int>>[]));
- buildStatusService = FakeBuildStatusService(
- commitStatuses: <CommitStatus>[
- CommitStatus(generateCommit(1, repo: 'engine', branch: 'main'), const <Stage>[]),
- ],
- );
- scheduler = Scheduler(
- cache: cache,
- config: config,
- buildStatusProvider: (_) => buildStatusService,
- datastoreProvider: (DatastoreDB db) => DatastoreService(db, 2),
- githubChecksService: GithubChecksService(config, githubChecksUtil: mockGithubChecksUtil),
- httpClientProvider: () => httpClient,
- luciBuildService: luciBuildService,
- );
-
- await scheduler.addCommits(createCommitList(<String>['1'], repo: 'engine', branch: 'main'));
- final List<dynamic> captured = verify(
- luciBuildService.schedulePostsubmitBuilds(
- commit: anyNamed('commit'),
- toBeScheduled: captureAnyNamed('toBeScheduled'),
- ),
- ).captured;
- final List<dynamic> toBeScheduled = captured.first as List<dynamic>;
- expect(toBeScheduled.length, 2);
- final Iterable<Tuple<Target, Task, int>> tuples =
- toBeScheduled.map((dynamic tuple) => tuple as Tuple<Target, Task, int>);
- final Iterable<String> scheduledTargetNames =
- tuples.map((Tuple<Target, Task, int> tuple) => tuple.second.name!);
- expect(scheduledTargetNames, ['Linux A', 'Linux runIf']);
- // Tasks triggered by cocoon are marked as in progress
- final Iterable<Task> tasks = db.values.values.whereType<Task>();
- expect(tasks.singleWhere((Task task) => task.name == 'Linux A').status, Task.statusInProgress);
- });
-
- test('schedules cocoon based targets - multiple batch requests', () async {
- final MockBuildBucketClient mockBuildBucketClient = MockBuildBucketClient();
- final FakeLuciBuildService luciBuildService = FakeLuciBuildService(
- config: config,
- buildbucket: mockBuildBucketClient,
- gerritService: FakeGerritService(),
- githubChecksUtil: mockGithubChecksUtil,
- pubsub: pubsub,
- );
- when(mockGithubChecksUtil.createCheckRun(any, any, any, any, output: anyNamed('output')))
- .thenAnswer((_) async => generateCheckRun(1, name: 'Linux A'));
- when(mockBuildBucketClient.listBuilders(any)).thenAnswer((_) async {
- return const ListBuildersResponse(
- builders: [
- BuilderItem(id: BuilderId(bucket: 'prod', project: 'flutter', builder: 'Linux A')),
- BuilderItem(id: BuilderId(bucket: 'prod', project: 'flutter', builder: 'Linux runIf')),
- ],
- );
- });
- buildStatusService = FakeBuildStatusService(
- commitStatuses: <CommitStatus>[
- CommitStatus(generateCommit(1, repo: 'engine', branch: 'main'), const <Stage>[]),
- ],
- );
- config.batchSizeValue = 1;
- scheduler = Scheduler(
- cache: cache,
- config: config,
- buildStatusProvider: (_) => buildStatusService,
- datastoreProvider: (DatastoreDB db) => DatastoreService(db, 2),
- githubChecksService: GithubChecksService(config, githubChecksUtil: mockGithubChecksUtil),
- httpClientProvider: () => httpClient,
- luciBuildService: luciBuildService,
- );
-
- await scheduler.addCommits(createCommitList(<String>['1'], repo: 'engine', branch: 'main'));
- expect(pubsub.messages.length, 2);
- });
- });
-
- group('add pull request', () {
- test('creates expected commit', () async {
- final PullRequest mergedPr = generatePullRequest();
- await scheduler.addPullRequest(mergedPr);
-
- expect(db.values.values.whereType<Commit>().length, 1);
- final Commit commit = db.values.values.whereType<Commit>().single;
- expect(commit.repository, 'flutter/flutter');
- expect(commit.branch, 'master');
- expect(commit.sha, 'abc');
- expect(commit.timestamp, 1);
- expect(commit.author, 'dash');
- expect(commit.authorAvatarUrl, 'dashatar');
- expect(commit.message, 'example message');
- });
-
- test('schedules tasks against merged PRs', () async {
- final PullRequest mergedPr = generatePullRequest();
- await scheduler.addPullRequest(mergedPr);
-
- expect(db.values.values.whereType<Commit>().length, 1);
- expect(db.values.values.whereType<Task>().length, 3);
- });
-
- test('guarantees scheduling of tasks against merged release branch PR', () async {
- final PullRequest mergedPr = generatePullRequest(branch: 'flutter-3.2-candidate.5');
- await scheduler.addPullRequest(mergedPr);
-
- expect(db.values.values.whereType<Commit>().length, 1);
- expect(db.values.values.whereType<Task>().length, 3);
- // Ensure all tasks have been marked in progress
- expect(db.values.values.whereType<Task>().where((Task task) => task.status == Task.statusNew), isEmpty);
- });
-
- test('guarantees scheduling of tasks against merged engine PR', () async {
- final PullRequest mergedPr = generatePullRequest(
- repo: Config.engineSlug.name,
- branch: Config.defaultBranch(Config.engineSlug),
- );
- await scheduler.addPullRequest(mergedPr);
-
- expect(db.values.values.whereType<Commit>().length, 1);
- expect(db.values.values.whereType<Task>().length, 3);
- // Ensure all tasks under cocoon scheduler have been marked in progress
- expect(db.values.values.whereType<Task>().where((Task task) => task.status == Task.statusInProgress).length, 2);
- });
-
- test('Release candidate branch commit filters builders not in default branch', () async {
- const String totCiYaml = r'''
-enabled_branches:
- - main
- - flutter-\d+\.\d+-candidate\.\d+
-targets:
- - name: Linux A
- properties:
- custom: abc
-''';
- httpClient = MockClient((http.Request request) async {
- if (request.url.path == '/flutter/engine/abc/.ci.yaml') {
- return http.Response(totCiYaml, HttpStatus.ok);
- }
- if (request.url.path == '/flutter/engine/1/.ci.yaml') {
- return http.Response(singleCiYaml, HttpStatus.ok);
- }
- print(request.url.path);
- throw Exception('Failed to find ${request.url.path}');
- });
- scheduler = Scheduler(
- cache: cache,
- config: config,
- datastoreProvider: (DatastoreDB db) => DatastoreService(db, 2),
- buildStatusProvider: (_) => buildStatusService,
- githubChecksService: GithubChecksService(config, githubChecksUtil: mockGithubChecksUtil),
- httpClientProvider: () => httpClient,
- luciBuildService: FakeLuciBuildService(
- config: config,
- githubChecksUtil: mockGithubChecksUtil,
- gerritService: FakeGerritService(
- branchesValue: <String>['master', 'main'],
- ),
- ),
- );
-
- final PullRequest mergedPr = generatePullRequest(
- repo: Config.engineSlug.name,
- branch: 'flutter-3.10-candidate.1',
- );
- await scheduler.addPullRequest(mergedPr);
-
- final List<Task> tasks = db.values.values.whereType<Task>().toList();
- expect(db.values.values.whereType<Commit>().length, 1);
- expect(tasks, hasLength(1));
- expect(tasks.first.name, 'Linux A');
- // Ensure all tasks under cocoon scheduler have been marked in progress
- expect(db.values.values.whereType<Task>().where((Task task) => task.status == Task.statusInProgress).length, 1);
- });
-
- test('does not schedule tasks against non-merged PRs', () async {
- final PullRequest notMergedPr = generatePullRequest(merged: false);
- await scheduler.addPullRequest(notMergedPr);
-
- expect(db.values.values.whereType<Commit>().map<String>(toSha).length, 0);
- expect(db.values.values.whereType<Task>().length, 0);
- });
-
- test('does not schedule tasks against already added PRs', () async {
- // Existing commits should not be duplicated.
- final Commit commit = shaToCommit('1');
- db.values[commit.key] = commit;
-
- final PullRequest alreadyLandedPr = generatePullRequest(sha: '1');
- await scheduler.addPullRequest(alreadyLandedPr);
-
- expect(db.values.values.whereType<Commit>().map<String>(toSha).length, 1);
- // No tasks should be scheduled as that is done on commit insert.
- expect(db.values.values.whereType<Task>().length, 0);
- });
-
- test('creates expected commit from release branch PR', () async {
- final PullRequest mergedPr = generatePullRequest(branch: '1.26');
- await scheduler.addPullRequest(mergedPr);
-
- expect(db.values.values.whereType<Commit>().length, 1);
- final Commit commit = db.values.values.whereType<Commit>().single;
- expect(commit.repository, 'flutter/flutter');
- expect(commit.branch, '1.26');
- expect(commit.sha, 'abc');
- expect(commit.timestamp, 1);
- expect(commit.author, 'dash');
- expect(commit.authorAvatarUrl, 'dashatar');
- expect(commit.message, 'example message');
- });
- });
-
- group('process check run', () {
- test('rerequested ci.yaml check retriggers presubmit', () async {
- final MockGithubService mockGithubService = MockGithubService();
- final MockGitHub mockGithubClient = MockGitHub();
- buildStatusService =
- FakeBuildStatusService(commitStatuses: <CommitStatus>[CommitStatus(generateCommit(1), const <Stage>[])]);
- config = FakeConfig(
- githubService: mockGithubService,
- );
- scheduler = Scheduler(
- cache: cache,
- config: config,
- buildStatusProvider: (_) => buildStatusService,
- githubChecksService: GithubChecksService(config, githubChecksUtil: mockGithubChecksUtil),
- httpClientProvider: () => httpClient,
- luciBuildService: FakeLuciBuildService(
- config: config,
- githubChecksUtil: mockGithubChecksUtil,
- ),
- );
- when(mockGithubService.github).thenReturn(mockGithubClient);
- when(mockGithubService.searchIssuesAndPRs(any, any, sort: anyNamed('sort'), pages: anyNamed('pages')))
- .thenAnswer((_) async => [generateIssue(3)]);
- when(mockGithubChecksUtil.listCheckSuitesForRef(any, any, ref: anyNamed('ref'))).thenAnswer(
- (_) async => [
- // From check_run.check_suite.id in [checkRunString].
- generateCheckSuite(668083231),
- ],
- );
- when(mockGithubService.getPullRequest(any, any)).thenAnswer((_) async => generatePullRequest());
- when(mockGithubService.listFiles(any)).thenAnswer((_) async => ['abc/def']);
- when(
- mockGithubChecksUtil.createCheckRun(
- any,
- any,
- any,
- any,
- output: anyNamed('output'),
- ),
- ).thenAnswer((_) async {
- return CheckRun.fromJson(const <String, dynamic>{
- 'id': 1,
- 'started_at': '2020-05-10T02:49:31Z',
- 'name': Scheduler.kCiYamlCheckName,
- 'check_suite': <String, dynamic>{'id': 2},
- });
- });
- final Map<String, dynamic> checkRunEventJson = jsonDecode(checkRunString) as Map<String, dynamic>;
- checkRunEventJson['check_run']['name'] = Scheduler.kCiYamlCheckName;
- final cocoon_checks.CheckRunEvent checkRunEvent = cocoon_checks.CheckRunEvent.fromJson(checkRunEventJson);
- expect(await scheduler.processCheckRun(checkRunEvent), true);
- verify(
- mockGithubChecksUtil.createCheckRun(
- any,
- any,
- any,
- Scheduler.kCiYamlCheckName,
- output: anyNamed('output'),
- ),
- );
- // Verfies Linux A was created
- verify(mockGithubChecksUtil.createCheckRun(any, any, any, any)).called(1);
- });
-
- test('rerequested presubmit check triggers presubmit build', () async {
- // Note that we're not inserting any commits into the db, because
- // only postsubmit commits are stored in the datastore.
- config = FakeConfig(dbValue: db);
- db = FakeDatastoreDB();
-
- // Set up mock buildbucket to validate which bucket is requested.
- final MockBuildBucketClient mockBuildbucket = MockBuildBucketClient();
- when(mockBuildbucket.batch(any)).thenAnswer((i) async {
- return FakeBuildBucketClient().batch(i.positionalArguments[0]);
- });
- when(mockBuildbucket.scheduleBuild(any, buildBucketUri: anyNamed('buildBucketUri')))
- .thenAnswer((realInvocation) async {
- final ScheduleBuildRequest scheduleBuildRequest = realInvocation.positionalArguments[0];
- // Ensure this is an attempt to schedule a presubmit build by
- // verifying that bucket == 'try'.
- expect(scheduleBuildRequest.builderId.bucket, equals('try'));
- return const Build(builderId: BuilderId(), id: '');
- });
-
- scheduler = Scheduler(
- cache: cache,
- config: config,
- githubChecksService: GithubChecksService(config, githubChecksUtil: mockGithubChecksUtil),
- luciBuildService: FakeLuciBuildService(
- config: config,
- githubChecksUtil: mockGithubChecksUtil,
- buildbucket: mockBuildbucket,
- ),
- );
- final cocoon_checks.CheckRunEvent checkRunEvent = cocoon_checks.CheckRunEvent.fromJson(
- jsonDecode(checkRunString) as Map<String, dynamic>,
- );
- expect(await scheduler.processCheckRun(checkRunEvent), true);
- verify(mockBuildbucket.scheduleBuild(any, buildBucketUri: anyNamed('buildBucketUri'))).called(1);
- verify(mockGithubChecksUtil.createCheckRun(any, any, any, any)).called(1);
- });
-
- test('rerequested postsubmit check triggers postsubmit build', () async {
- // Set up datastore with postsubmit entities matching [checkRunString].
- db = FakeDatastoreDB();
- config = FakeConfig(dbValue: db, postsubmitSupportedReposValue: {RepositorySlug('abc', 'cocoon')});
- final Commit commit = generateCommit(
- 1,
- sha: '66d6bd9a3f79a36fe4f5178ccefbc781488a596c',
- branch: 'independent_agent',
- owner: 'abc',
- repo: 'cocoon',
- );
- final Commit commitToT = generateCommit(
- 1,
- sha: '66d6bd9a3f79a36fe4f5178ccefbc781488a592c',
- branch: 'master',
- owner: 'abc',
- repo: 'cocoon',
- );
- config.db.values[commit.key] = commit;
- config.db.values[commitToT.key] = commitToT;
- final Task task = generateTask(1, name: 'test1', parent: commit);
- config.db.values[task.key] = task;
-
- // Set up ci.yaml with task name and branch name from [checkRunString].
- httpClient = MockClient((http.Request request) async {
- if (request.url.path.contains('.ci.yaml')) {
- return http.Response(
- r'''
-enabled_branches:
- - independent_agent
- - master
-targets:
- - name: test1
-''',
- 200,
- );
- }
- throw Exception('Failed to find ${request.url.path}');
- });
-
- // Set up mock buildbucket to validate which bucket is requested.
- final MockBuildBucketClient mockBuildbucket = MockBuildBucketClient();
- when(mockBuildbucket.batch(any)).thenAnswer((i) async {
- return FakeBuildBucketClient().batch(i.positionalArguments[0]);
- });
- when(mockBuildbucket.scheduleBuild(any, buildBucketUri: anyNamed('buildBucketUri')))
- .thenAnswer((realInvocation) async {
- final ScheduleBuildRequest scheduleBuildRequest = realInvocation.positionalArguments[0];
- // Ensure this is an attempt to schedule a postsubmit build by
- // verifying that bucket == 'prod'.
- expect(scheduleBuildRequest.builderId.bucket, equals('prod'));
- return const Build(builderId: BuilderId(), id: '');
- });
-
- scheduler = Scheduler(
- cache: cache,
- config: config,
- githubChecksService: GithubChecksService(config, githubChecksUtil: mockGithubChecksUtil),
- httpClientProvider: () => httpClient,
- luciBuildService: FakeLuciBuildService(
- config: config,
- githubChecksUtil: mockGithubChecksUtil,
- buildbucket: mockBuildbucket,
- gerritService: FakeGerritService(
- branchesValue: <String>['master', 'main'],
- ),
- ),
- );
- final cocoon_checks.CheckRunEvent checkRunEvent = cocoon_checks.CheckRunEvent.fromJson(
- jsonDecode(checkRunString) as Map<String, dynamic>,
- );
- expect(await scheduler.processCheckRun(checkRunEvent), true);
- verify(mockBuildbucket.scheduleBuild(any, buildBucketUri: anyNamed('buildBucketUri'))).called(1);
- verify(mockGithubChecksUtil.createCheckRun(any, any, any, any)).called(1);
- });
-
- test('rerequested does not fail on empty pull request list', () async {
- when(mockGithubChecksUtil.createCheckRun(any, any, any, any)).thenAnswer((_) async {
- return CheckRun.fromJson(const <String, dynamic>{
- 'id': 1,
- 'started_at': '2020-05-10T02:49:31Z',
- 'check_suite': <String, dynamic>{'id': 2},
- });
- });
- final cocoon_checks.CheckRunEvent checkRunEvent = cocoon_checks.CheckRunEvent.fromJson(
- jsonDecode(checkRunWithEmptyPullRequests) as Map<String, dynamic>,
- );
- expect(await scheduler.processCheckRun(checkRunEvent), true);
- verify(mockGithubChecksUtil.createCheckRun(any, any, any, any)).called(1);
- });
- });
-
- group('presubmit', () {
- test('gets only enabled .ci.yaml builds', () async {
- httpClient = MockClient((http.Request request) async {
- if (request.url.path.contains('.ci.yaml')) {
- return http.Response(
- '''
-enabled_branches:
- - master
-targets:
- - name: Linux A
- presubmit: true
- scheduler: luci
- - name: Linux B
- scheduler: luci
- enabled_branches:
- - stable
- presubmit: true
- - name: Linux C
- scheduler: luci
- enabled_branches:
- - master
- presubmit: true
- - name: Linux D
- scheduler: luci
- bringup: true
- presubmit: true
- - name: Google-internal roll
- scheduler: google_internal
- enabled_branches:
- - master
- presubmit: true
- ''',
- 200,
- );
- }
- throw Exception('Failed to find ${request.url.path}');
- });
- final List<Target> presubmitTargets = await scheduler.getPresubmitTargets(pullRequest);
- expect(
- presubmitTargets.map((Target target) => target.value.name).toList(),
- containsAll(<String>['Linux A', 'Linux C']),
- );
- });
-
- test('checks for release branches', () async {
- const String branch = 'flutter-1.24-candidate.1';
- httpClient = MockClient((http.Request request) async {
- if (request.url.path.contains('.ci.yaml')) {
- return http.Response(
- '''
-enabled_branches:
- - master
-targets:
- - name: Linux A
- presubmit: true
- scheduler: luci
- ''',
- 200,
- );
- }
- throw Exception('Failed to find ${request.url.path}');
- });
- expect(
- scheduler.getPresubmitTargets(generatePullRequest(branch: branch)),
- throwsA(predicate((Exception e) => e.toString().contains('$branch is not enabled'))),
- );
- });
-
- test('checks for release branch regex', () async {
- const String branch = 'flutter-1.24-candidate.1';
- httpClient = MockClient((http.Request request) async {
- if (request.url.path.contains('.ci.yaml')) {
- return http.Response(
- '''
-enabled_branches:
- - main
- - master
- - flutter-\\d+.\\d+-candidate.\\d+
-targets:
- - name: Linux A
- scheduler: luci
- ''',
- 200,
- );
- }
- throw Exception('Failed to find ${request.url.path}');
- });
- final List<Target> targets = await scheduler.getPresubmitTargets(generatePullRequest(branch: branch));
- expect(targets.single.value.name, 'Linux A');
- });
-
- test('triggers expected presubmit build checks', () async {
- await scheduler.triggerPresubmitTargets(pullRequest: pullRequest);
- expect(
- verify(mockGithubChecksUtil.createCheckRun(any, any, any, captureAny, output: captureAnyNamed('output')))
- .captured,
- <dynamic>[
- Scheduler.kCiYamlCheckName,
- const CheckRunOutput(
- title: Scheduler.kCiYamlCheckName,
- summary: 'If this check is stuck pending, push an empty commit to retrigger the checks',
- ),
- 'Linux A',
- null,
- // Linux runIf is not run as this is for tip of tree and the files weren't affected
- ],
- );
- });
-
- test('Do not schedule other targets on revert request.', () async {
- final PullRequest releasePullRequest = generatePullRequest(
- labels: [IssueLabel(name: 'revert of')],
- );
-
- releasePullRequest.user = User(login: 'auto-submit[bot]');
-
- await scheduler.triggerPresubmitTargets(pullRequest: releasePullRequest);
- expect(
- verify(mockGithubChecksUtil.createCheckRun(any, any, any, captureAny, output: captureAnyNamed('output')))
- .captured,
- <dynamic>[
- Scheduler.kCiYamlCheckName,
- // No other targets should be created.
- const CheckRunOutput(
- title: Scheduler.kCiYamlCheckName,
- summary: 'If this check is stuck pending, push an empty commit to retrigger the checks',
- ),
- ],
- );
- });
-
- test('filters out presubmit targets that do not exist in main and do not filter targets not in main', () async {
- const String singleCiYaml = r'''
-enabled_branches:
- - master
- - main
- - flutter-\d+\.\d+-candidate\.\d+
-targets:
- - name: Linux A
- properties:
- custom: abc
- - name: Linux B
- enabled_branches:
- - flutter-\d+\.\d+-candidate\.\d+
- scheduler: luci
- - name: Linux C
- enabled_branches:
- - main
- - flutter-\d+\.\d+-candidate\.\d+
- scheduler: luci
-''';
- const String totCiYaml = r'''
-enabled_branches:
- - main
- - flutter-\d+\.\d+-candidate\.\d+
-targets:
- - name: Linux A
- bringup: true
- properties:
- custom: abc
-''';
- httpClient = MockClient((http.Request request) async {
- if (request.url.path == '/flutter/engine/1/.ci.yaml') {
- return http.Response(totCiYaml, HttpStatus.ok);
- }
- if (request.url.path == '/flutter/engine/abc/.ci.yaml') {
- return http.Response(singleCiYaml, HttpStatus.ok);
- }
- print(request.url.path);
- throw Exception('Failed to find ${request.url.path}');
- });
- scheduler = Scheduler(
- cache: cache,
- config: config,
- datastoreProvider: (DatastoreDB db) => DatastoreService(db, 2),
- buildStatusProvider: (_) => buildStatusService,
- githubChecksService: GithubChecksService(config, githubChecksUtil: mockGithubChecksUtil),
- httpClientProvider: () => httpClient,
- luciBuildService: FakeLuciBuildService(
- config: config,
- githubChecksUtil: mockGithubChecksUtil,
- gerritService: FakeGerritService(
- branchesValue: <String>['master', 'main'],
- ),
- ),
- );
- final PullRequest pr = generatePullRequest(
- repo: Config.engineSlug.name,
- branch: 'flutter-3.10-candidate.1',
- );
- final List<Target> targets = await scheduler.getPresubmitTargets(pr);
- expect(
- targets.map((Target target) => target.value.name).toList(),
- containsAll(<String>['Linux A', 'Linux B']),
- );
- });
-
- test('triggers all presubmit build checks when diff cannot be found', () async {
- final MockGithubService mockGithubService = MockGithubService();
- when(mockGithubService.listFiles(pullRequest))
- .thenThrow(GitHubError(GitHub(), 'Requested Resource was Not Found'));
- buildStatusService =
- FakeBuildStatusService(commitStatuses: <CommitStatus>[CommitStatus(generateCommit(1), const <Stage>[])]);
- scheduler = Scheduler(
- cache: cache,
- config: FakeConfig(
- // tabledataResource: tabledataResource,
- dbValue: db,
- githubService: mockGithubService,
- githubClient: MockGitHub(),
- ),
- buildStatusProvider: (_) => buildStatusService,
- datastoreProvider: (DatastoreDB db) => DatastoreService(db, 2),
- githubChecksService: GithubChecksService(config, githubChecksUtil: mockGithubChecksUtil),
- httpClientProvider: () => httpClient,
- luciBuildService: FakeLuciBuildService(
- config: config,
- githubChecksUtil: mockGithubChecksUtil,
- gerritService: FakeGerritService(branchesValue: <String>['master']),
- ),
- );
- await scheduler.triggerPresubmitTargets(pullRequest: pullRequest);
- expect(
- verify(mockGithubChecksUtil.createCheckRun(any, any, any, captureAny, output: captureAnyNamed('output')))
- .captured,
- <dynamic>[
- Scheduler.kCiYamlCheckName,
- const CheckRunOutput(
- title: Scheduler.kCiYamlCheckName,
- summary: 'If this check is stuck pending, push an empty commit to retrigger the checks',
- ),
- 'Linux A',
- null,
- // runIf requires a diff in dev, so an error will cause it to be triggered
- 'Linux runIf',
- null,
- ],
- );
- });
-
- test('triggers all presubmit targets on release branch pull request', () async {
- final PullRequest releasePullRequest = generatePullRequest(
- branch: 'flutter-1.24-candidate.1',
- );
- await scheduler.triggerPresubmitTargets(pullRequest: releasePullRequest);
- expect(
- verify(mockGithubChecksUtil.createCheckRun(any, any, any, captureAny, output: captureAnyNamed('output')))
- .captured,
- <dynamic>[
- Scheduler.kCiYamlCheckName,
- const CheckRunOutput(
- title: Scheduler.kCiYamlCheckName,
- summary: 'If this check is stuck pending, push an empty commit to retrigger the checks',
- ),
- 'Linux A',
- null,
- 'Linux runIf',
- null,
- ],
- );
- });
-
- test('ci.yaml validation passes with default config', () async {
- when(mockGithubChecksUtil.getCheckRun(any, any, any))
- .thenAnswer((Invocation invocation) async => createCheckRun(id: 0));
- await scheduler.triggerPresubmitTargets(pullRequest: pullRequest);
- expect(
- verify(
- mockGithubChecksUtil.updateCheckRun(
- any,
- any,
- any,
- status: captureAnyNamed('status'),
- conclusion: captureAnyNamed('conclusion'),
- output: anyNamed('output'),
- ),
- ).captured,
- <dynamic>[CheckRunStatus.completed, CheckRunConclusion.success],
- );
- });
-
- test('ci.yaml validation fails with empty config', () async {
- httpClient = MockClient((http.Request request) async {
- if (request.url.path.contains('.ci.yaml')) {
- return http.Response('', 200);
- }
- throw Exception('Failed to find ${request.url.path}');
- });
- await scheduler.triggerPresubmitTargets(pullRequest: pullRequest);
- expect(
- verify(
- mockGithubChecksUtil.updateCheckRun(
- any,
- any,
- any,
- status: captureAnyNamed('status'),
- conclusion: captureAnyNamed('conclusion'),
- output: anyNamed('output'),
- ),
- ).captured,
- <dynamic>[CheckRunStatus.completed, CheckRunConclusion.failure],
- );
- });
-
- test('ci.yaml validation fails on not enabled branch', () async {
- final PullRequest pullRequest = generatePullRequest(branch: 'not-valid');
- await scheduler.triggerPresubmitTargets(pullRequest: pullRequest);
- expect(
- verify(
- mockGithubChecksUtil.updateCheckRun(
- any,
- any,
- any,
- status: captureAnyNamed('status'),
- conclusion: captureAnyNamed('conclusion'),
- output: anyNamed('output'),
- ),
- ).captured,
- <dynamic>[CheckRunStatus.completed, CheckRunConclusion.failure],
- );
- });
-
- test('ci.yaml validation fails with config with unknown dependencies', () async {
- httpClient = MockClient((http.Request request) async {
- if (request.url.path.contains('.ci.yaml')) {
- return http.Response(
- '''
-enabled_branches:
- - master
-targets:
- - name: A
- builder: Linux A
- dependencies:
- - B
- ''',
- 200,
- );
- }
- throw Exception('Failed to find ${request.url.path}');
- });
- await scheduler.triggerPresubmitTargets(pullRequest: pullRequest);
- expect(
- verify(
- mockGithubChecksUtil.updateCheckRun(
- any,
- any,
- any,
- status: anyNamed('status'),
- conclusion: anyNamed('conclusion'),
- output: captureAnyNamed('output'),
- ),
- ).captured.first.text,
- 'FormatException: ERROR: A depends on B which does not exist',
- );
- });
-
- test('retries only triggers failed builds only', () async {
- final MockBuildBucketClient mockBuildbucket = MockBuildBucketClient();
- buildStatusService =
- FakeBuildStatusService(commitStatuses: <CommitStatus>[CommitStatus(generateCommit(1), const <Stage>[])]);
- final FakePubSub pubsub = FakePubSub();
- scheduler = Scheduler(
- cache: cache,
- config: config,
- datastoreProvider: (DatastoreDB db) => DatastoreService(db, 2),
- githubChecksService: GithubChecksService(config, githubChecksUtil: mockGithubChecksUtil),
- buildStatusProvider: (_) => buildStatusService,
- httpClientProvider: () => httpClient,
- luciBuildService: FakeLuciBuildService(
- config: config,
- githubChecksUtil: mockGithubChecksUtil,
- buildbucket: mockBuildbucket,
- gerritService: FakeGerritService(branchesValue: <String>['master']),
- pubsub: pubsub,
- ),
- );
- when(mockBuildbucket.batch(any)).thenAnswer(
- (_) async => BatchResponse(
- responses: <Response>[
- Response(
- searchBuilds: SearchBuildsResponse(
- builds: <Build>[
- generateBuild(1000, name: 'Linux', bucket: 'try'),
- generateBuild(2000, name: 'Linux Coverage', bucket: 'try'),
- generateBuild(3000, name: 'Mac', bucket: 'try', status: Status.scheduled),
- generateBuild(4000, name: 'Windows', bucket: 'try', status: Status.started),
- generateBuild(5000, name: 'Linux A', bucket: 'try', status: Status.failure),
- ],
- ),
- ),
- ],
- ),
- );
- when(mockBuildbucket.scheduleBuild(any))
- .thenAnswer((_) async => generateBuild(5001, name: 'Linux A', bucket: 'try', status: Status.scheduled));
- // Only Linux A should be retried
- final Map<String, CheckRun> checkRuns = <String, CheckRun>{
- 'Linux': createCheckRun(name: 'Linux', id: 100),
- 'Linux Coverage': createCheckRun(name: 'Linux Coverage', id: 200),
- 'Mac': createCheckRun(name: 'Mac', id: 300, status: CheckRunStatus.queued),
- 'Windows': createCheckRun(name: 'Windows', id: 400, status: CheckRunStatus.inProgress),
- 'Linux A': createCheckRun(name: 'Linux A', id: 500),
- };
- when(mockGithubChecksUtil.allCheckRuns(any, any)).thenAnswer((_) async {
- return checkRuns;
- });
-
- final CheckSuiteEvent checkSuiteEvent =
- CheckSuiteEvent.fromJson(jsonDecode(checkSuiteTemplate('rerequested')) as Map<String, dynamic>);
- await scheduler.retryPresubmitTargets(
- pullRequest: pullRequest,
- checkSuiteEvent: checkSuiteEvent,
- );
-
- expect(pubsub.messages.length, 1);
- final BatchRequest batchRequest = pubsub.messages.single as BatchRequest;
- expect(batchRequest.requests!.length, 1);
- // Schedule build should have been sent
- expect(batchRequest.requests!.single.scheduleBuild, isNotNull);
- final ScheduleBuildRequest scheduleBuildRequest = batchRequest.requests!.single.scheduleBuild!;
- // Verify expected parameters to schedule build
- expect(scheduleBuildRequest.builderId.builder, 'Linux A');
- expect(scheduleBuildRequest.properties!['custom'], 'abc');
- });
-
- test('pass github_build_label to properties', () async {
- final MockBuildBucketClient mockBuildbucket = MockBuildBucketClient();
- buildStatusService =
- FakeBuildStatusService(commitStatuses: <CommitStatus>[CommitStatus(generateCommit(1), const <Stage>[])]);
- final FakePubSub pubsub = FakePubSub();
- scheduler = Scheduler(
- cache: cache,
- config: config,
- datastoreProvider: (DatastoreDB db) => DatastoreService(db, 2),
- githubChecksService: GithubChecksService(config, githubChecksUtil: mockGithubChecksUtil),
- buildStatusProvider: (_) => buildStatusService,
- httpClientProvider: () => httpClient,
- luciBuildService: FakeLuciBuildService(
- config: config,
- githubChecksUtil: mockGithubChecksUtil,
- buildbucket: mockBuildbucket,
- gerritService: FakeGerritService(branchesValue: <String>['master']),
- pubsub: pubsub,
- ),
- );
- when(mockBuildbucket.batch(any)).thenAnswer(
- (_) async => BatchResponse(
- responses: <Response>[
- Response(
- searchBuilds: SearchBuildsResponse(
- builds: <Build>[
- generateBuild(1000, name: 'Linux', bucket: 'try'),
- generateBuild(2000, name: 'Linux Coverage', bucket: 'try'),
- generateBuild(3000, name: 'Mac', bucket: 'try', status: Status.scheduled),
- generateBuild(4000, name: 'Windows', bucket: 'try', status: Status.started),
- generateBuild(5000, name: 'Linux A', bucket: 'try', status: Status.failure),
- ],
- ),
- ),
- ],
- ),
- );
- when(mockBuildbucket.scheduleBuild(any))
- .thenAnswer((_) async => generateBuild(5001, name: 'Linux A', bucket: 'try', status: Status.scheduled));
- // Only Linux A should be retried
- final Map<String, CheckRun> checkRuns = <String, CheckRun>{
- 'Linux': createCheckRun(name: 'Linux', id: 100),
- 'Linux Coverage': createCheckRun(name: 'Linux Coverage', id: 200),
- 'Mac': createCheckRun(name: 'Mac', id: 300, status: CheckRunStatus.queued),
- 'Windows': createCheckRun(name: 'Windows', id: 400, status: CheckRunStatus.inProgress),
- 'Linux A': createCheckRun(name: 'Linux A', id: 500),
- };
- when(mockGithubChecksUtil.allCheckRuns(any, any)).thenAnswer((_) async {
- return checkRuns;
- });
-
- final CheckSuiteEvent checkSuiteEvent =
- CheckSuiteEvent.fromJson(jsonDecode(checkSuiteTemplate('rerequested')) as Map<String, dynamic>);
- await scheduler.retryPresubmitTargets(
- pullRequest: pullRequest,
- checkSuiteEvent: checkSuiteEvent,
- );
-
- expect(pubsub.messages.length, 1);
- final BatchRequest batchRequest = pubsub.messages.single as BatchRequest;
- expect(batchRequest.requests!.length, 1);
- // Schedule build should have been sent
- expect(batchRequest.requests!.single.scheduleBuild, isNotNull);
- final ScheduleBuildRequest scheduleBuildRequest = batchRequest.requests!.single.scheduleBuild!;
- // Verify expected parameters to schedule build
- expect(scheduleBuildRequest.builderId.builder, 'Linux A');
- expect(scheduleBuildRequest.properties!['custom'], 'abc');
- });
-
- test('triggers only specificed targets', () async {
- final List<Target> presubmitTargets = <Target>[generateTarget(1), generateTarget(2)];
- final List<Target> presubmitTriggerTargets = scheduler.getTriggerList(presubmitTargets, <String>['Linux 1']);
- expect(presubmitTriggerTargets.length, 1);
- });
-
- test('triggers all presubmit targets when trigger list is null', () async {
- final List<Target> presubmitTargets = <Target>[generateTarget(1), generateTarget(2)];
- final List<Target> presubmitTriggerTargets = scheduler.getTriggerList(presubmitTargets, null);
- expect(presubmitTriggerTargets.length, 2);
- });
-
- test('triggers all presubmit targets when trigger list is empty', () async {
- final List<Target> presubmitTargets = <Target>[generateTarget(1), generateTarget(2)];
- final List<Target> presubmitTriggerTargets = scheduler.getTriggerList(presubmitTargets, <String>[]);
- expect(presubmitTriggerTargets.length, 2);
- });
-
- test('triggers only targets that are contained in the trigger list', () async {
- final List<Target> presubmitTargets = <Target>[generateTarget(1), generateTarget(2)];
- final List<Target> presubmitTriggerTargets =
- scheduler.getTriggerList(presubmitTargets, <String>['Linux 1', 'Linux 3']);
- expect(presubmitTriggerTargets.length, 1);
- expect(presubmitTargets[0].value.name, 'Linux 1');
- });
- });
- });
-}
-
-CheckRun createCheckRun({String? name, required int id, CheckRunStatus status = CheckRunStatus.completed}) {
- final int externalId = id * 2;
- final String checkRunJson =
- '{"name": "$name", "id": $id, "external_id": "{$externalId}", "status": "$status", "started_at": "2020-05-10T02:49:31Z", "head_sha": "the_sha", "check_suite": {"id": 456}}';
- return CheckRun.fromJson(jsonDecode(checkRunJson) as Map<String, dynamic>);
-}
-
-String toSha(Commit commit) => commit.sha!;
-
-int toTimestamp(Commit commit) => commit.timestamp!;
diff --git a/app_dart/test/src/bigquery/fake_tabledata_resource.dart b/app_dart/test/src/bigquery/fake_tabledata_resource.dart
deleted file mode 100644
index 9b7581f..0000000
--- a/app_dart/test/src/bigquery/fake_tabledata_resource.dart
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:googleapis/bigquery/v2.dart';
-
-/// A fake bigquery tabledataresourceApi implementation.
-///
-/// This tabledataResourceApi considers only a simple case
-/// where we focus on the number of rows inserted. This can be
-/// easily extended for other test cases.
-class FakeTabledataResource implements TabledataResource {
- List<TableDataInsertAllRequestRows>? rows;
- @override
- Future<TableDataInsertAllResponse> insertAll(
- TableDataInsertAllRequest request,
- String projectId,
- String datasetId,
- String tableId, {
- String? $fields,
- }) async {
- rows = request.rows;
- return TableDataInsertAllResponse.fromJson(<String, String>{});
- }
-
- @override
- Future<TableDataList> list(
- String projectId,
- String datasetId,
- String tableId, {
- int? maxResults,
- String? selectedFields,
- String? startIndex,
- String? pageToken,
- String? $fields,
- }) async {
- final List<Map<String, Object>> tableRowList = <Map<String, Object>>[];
- for (TableDataInsertAllRequestRows tableDataInsertAllRequestRows in rows!) {
- final dynamic value = tableDataInsertAllRequestRows.json;
- final List<Map<String, Object?>> tableCellList = <Map<String, Object?>>[];
- tableCellList.add(<String, Object?>{'v': value});
- tableRowList.add(<String, Object>{'f': tableCellList});
- }
-
- return TableDataList.fromJson(<String, Object>{'totalRows': rows!.length.toString(), 'rows': tableRowList});
- }
-}
diff --git a/app_dart/test/src/datastore/fake_config.dart b/app_dart/test/src/datastore/fake_config.dart
deleted file mode 100644
index 5ef2add..0000000
--- a/app_dart/test/src/datastore/fake_config.dart
+++ /dev/null
@@ -1,305 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-
-import 'package:appengine/appengine.dart';
-import 'package:cocoon_service/src/model/appengine/branch.dart';
-import 'package:cocoon_service/src/model/appengine/key_helper.dart';
-import 'package:cocoon_service/src/model/appengine/service_account_info.dart';
-import 'package:cocoon_service/src/service/bigquery.dart';
-import 'package:cocoon_service/src/service/config.dart';
-import 'package:cocoon_service/src/service/github_service.dart';
-import 'package:github/github.dart' as gh;
-import 'package:googleapis/bigquery/v2.dart';
-import 'package:graphql/client.dart';
-
-import '../request_handling/fake_authentication.dart';
-import '../service/fake_github_service.dart';
-import 'fake_datastore.dart';
-
-// ignore: must_be_immutable
-class FakeConfig implements Config {
- FakeConfig({
- this.githubClient,
- this.deviceLabServiceAccountValue,
- this.maxTaskRetriesValue,
- this.maxLuciTaskRetriesValue,
- this.keyHelperValue,
- this.oauthClientIdValue,
- this.githubOAuthTokenValue,
- this.mergeConflictPullRequestMessageValue = 'default mergeConflictPullRequestMessageValue',
- this.missingTestsPullRequestMessageValue = 'default missingTestsPullRequestMessageValue',
- this.wrongBaseBranchPullRequestMessageValue,
- this.wrongHeadBranchPullRequestMessageValue,
- this.releaseBranchPullRequestMessageValue,
- this.webhookKeyValue,
- this.loggingServiceValue,
- this.tabledataResource,
- this.githubService,
- this.bigqueryService,
- this.githubGraphQLClient,
- this.rollerAccountsValue,
- this.flutterBuildValue,
- this.flutterBuildDescriptionValue,
- this.maxRecordsValue,
- this.flutterGoldPendingValue,
- this.flutterGoldSuccessValue,
- this.flutterGoldChangesValue,
- this.flutterGoldAlertConstantValue,
- this.flutterGoldInitialAlertValue,
- this.flutterGoldFollowUpAlertValue,
- this.flutterGoldDraftChangeValue,
- this.flutterGoldStalePRValue,
- this.postsubmitSupportedReposValue,
- this.supportedBranchesValue,
- this.supportedReposValue,
- this.batchSizeValue,
- this.backfillerTargetLimitValue,
- this.backfillerCommitLimitValue,
- this.issueAndPRLimitValue,
- this.githubRequestDelayValue,
- FakeDatastoreDB? dbValue,
- }) : dbValue = dbValue ?? FakeDatastoreDB();
-
- gh.GitHub? githubClient;
- GraphQLClient? githubGraphQLClient;
- TabledataResource? tabledataResource;
- BigqueryService? bigqueryService;
- GithubService? githubService;
- FakeDatastoreDB dbValue;
- ServiceAccountInfo? deviceLabServiceAccountValue;
- int? maxTaskRetriesValue;
- int? maxLuciTaskRetriesValue;
- int? batchSizeValue;
- FakeKeyHelper? keyHelperValue;
- String? oauthClientIdValue;
- String? githubOAuthTokenValue;
- String mergeConflictPullRequestMessageValue;
- String missingTestsPullRequestMessageValue;
- String? wrongBaseBranchPullRequestMessageValue;
- String? wrongHeadBranchPullRequestMessageValue;
- String? releaseBranchPullRequestMessageValue;
- String? webhookKeyValue;
- String? flutterBuildValue;
- String? flutterBuildDescriptionValue;
- Logging? loggingServiceValue;
- String? waitingForTreeToGoGreenLabelNameValue;
- Set<String>? rollerAccountsValue;
- int? maxRecordsValue;
- int? backfillerTargetLimitValue;
- int? backfillerCommitLimitValue;
- int? issueAndPRLimitValue;
- String? flutterGoldPendingValue;
- String? flutterGoldSuccessValue;
- String? flutterGoldChangesValue;
- String? flutterGoldAlertConstantValue;
- String? flutterGoldInitialAlertValue;
- String? flutterGoldFollowUpAlertValue;
- String? flutterGoldDraftChangeValue;
- String? flutterGoldStalePRValue;
- List<String>? supportedBranchesValue;
- String? overrideTreeStatusLabelValue;
- Set<gh.RepositorySlug>? supportedReposValue;
- Set<gh.RepositorySlug>? postsubmitSupportedReposValue;
- Duration? githubRequestDelayValue;
-
- @override
- Future<gh.GitHub> createGitHubClient({gh.PullRequest? pullRequest, gh.RepositorySlug? slug}) async => githubClient!;
-
- @override
- gh.GitHub createGitHubClientWithToken(String token) => githubClient!;
-
- @override
- Future<GraphQLClient> createGitHubGraphQLClient() async => githubGraphQLClient!;
-
- @override
- Future<TabledataResource> createTabledataResourceApi() async => tabledataResource!;
-
- @override
- Future<BigqueryService> createBigQueryService() async => bigqueryService!;
-
- @override
- Future<GithubService> createGithubService(gh.RepositorySlug slug) async => githubService ?? FakeGithubService();
-
- @override
- GithubService createGithubServiceWithToken(String token) => githubService!;
-
- @override
- FakeDatastoreDB get db => dbValue;
-
- @override
- Duration get githubRequestDelay => githubRequestDelayValue ?? Duration.zero;
-
- @override
- int get maxTaskRetries => maxTaskRetriesValue!;
-
- /// Size of the shards to send to buildBucket when scheduling builds.
- @override
- int get schedulingShardSize => 5;
-
- @override
- int get backfillerTargetLimit => backfillerTargetLimitValue ?? 50;
-
- @override
- int get backfillerCommitLimit => backfillerCommitLimitValue ?? 50;
-
- @override
- int get issueAndPRLimit => issueAndPRLimitValue ?? 2;
-
- @override
- int get batchSize => batchSizeValue ?? 5;
-
- @override
- int get maxLuciTaskRetries => maxLuciTaskRetriesValue!;
-
- @override
- int get maxRecords => maxRecordsValue!;
-
- @override
- String get flutterGoldPending => flutterGoldPendingValue!;
-
- @override
- String get flutterGoldSuccess => flutterGoldSuccessValue!;
-
- @override
- String get flutterGoldChanges => flutterGoldChangesValue!;
-
- @override
- String get flutterGoldDraftChange => flutterGoldDraftChangeValue!;
-
- @override
- String get flutterGoldStalePR => flutterGoldStalePRValue!;
-
- @override
- String flutterGoldInitialAlert(String url) => flutterGoldInitialAlertValue!;
-
- @override
- String flutterGoldFollowUpAlert(String url) => flutterGoldFollowUpAlertValue!;
-
- @override
- String flutterGoldAlertConstant(gh.RepositorySlug slug) => flutterGoldAlertConstantValue!;
-
- @override
- String flutterGoldCommentID(gh.PullRequest pr) => 'PR ${pr.number}, at ${pr.head!.sha}';
-
- @override
- Future<String> get frobWebhookKey async => 'frob-webhook-key';
-
- @override
- int get commitNumber => 30;
-
- @override
- KeyHelper get keyHelper => keyHelperValue!;
-
- @override
- Future<String> get oauthClientId async => oauthClientIdValue!;
-
- @override
- Future<String> get githubOAuthToken async => githubOAuthTokenValue ?? 'token';
-
- @override
- String get mergeConflictPullRequestMessage => mergeConflictPullRequestMessageValue;
-
- @override
- String get missingTestsPullRequestMessage => missingTestsPullRequestMessageValue;
-
- @override
- String get wrongBaseBranchPullRequestMessage => wrongBaseBranchPullRequestMessageValue!;
-
- @override
- String wrongHeadBranchPullRequestMessage(String branch) => wrongHeadBranchPullRequestMessageValue!;
-
- @override
- String get releaseBranchPullRequestMessage => releaseBranchPullRequestMessageValue!;
-
- @override
- Future<String> get webhookKey async => webhookKeyValue!;
-
- @override
- String get flutterBuild => flutterBuildValue!;
-
- @override
- String get flutterBuildDescription =>
- flutterBuildDescriptionValue ??
- 'Tree is currently broken. Please do not merge this '
- 'PR unless it contains a fix for the tree.';
-
- @override
- Logging get loggingService => loggingServiceValue!;
-
- @override
- String get waitingForTreeToGoGreenLabelName => waitingForTreeToGoGreenLabelNameValue!;
-
- @override
- Set<String> get rollerAccounts => rollerAccountsValue!;
-
- @override
- Future<String> generateGithubToken(gh.RepositorySlug slug) {
- throw UnimplementedError();
- }
-
- @override
- Future<String> generateJsonWebToken() {
- throw UnimplementedError();
- }
-
- @override
- Future<String> get githubAppId => throw UnimplementedError();
-
- @override
- Future<Map<String, dynamic>> get githubAppInstallations => throw UnimplementedError();
-
- @override
- Future<String> get githubPrivateKey => throw UnimplementedError();
-
- @override
- Future<String> get githubPublicKey => throw UnimplementedError();
-
- @override
- Future<GithubService> createDefaultGitHubService() async => githubService!;
-
- @override
- Future<String> get overrideTreeStatusLabel async => overrideTreeStatusLabelValue!;
-
- @override
- String get defaultRecipeBundleRef => 'refs/heads/main';
-
- @override
- Future<List<String>> get releaseAccounts async => <String>['dart-flutter-releaser'];
-
- @override
- Set<gh.RepositorySlug> get supportedRepos =>
- supportedReposValue ??
- <gh.RepositorySlug>{
- Config.flutterSlug,
- Config.engineSlug,
- Config.cocoonSlug,
- Config.packagesSlug,
- };
-
- @override
- Set<gh.RepositorySlug> get postsubmitSupportedRepos =>
- postsubmitSupportedReposValue ?? <gh.RepositorySlug>{Config.packagesSlug};
-
- @override
- Future<Iterable<Branch>> getBranches(gh.RepositorySlug slug) async {
- if (supportedBranchesValue == null) {
- throw Exception('Test must set suportedBranchesValue to be able to use Config.getBranches');
- }
- return supportedBranchesValue!.map(
- (String branch) => Branch(
- key: db.emptyKey.append<String>(
- Branch,
- id: '${slug.fullName}/$branch',
- ),
- ),
- );
- }
-
- @override
- String get autosubmitBot => 'auto-submit[bot]';
-
- static const String revertOfLabel = 'revert of';
-}
diff --git a/app_dart/test/src/datastore/fake_datastore.dart b/app_dart/test/src/datastore/fake_datastore.dart
deleted file mode 100644
index f749dd4..0000000
--- a/app_dart/test/src/datastore/fake_datastore.dart
+++ /dev/null
@@ -1,336 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-import 'dart:math' as math;
-
-import 'package:gcloud/datastore.dart' show Datastore, OrderDirection, DatastoreError;
-import 'package:gcloud/db.dart';
-
-/// Signature for a callback function that will be notified whenever a
-/// [FakeQuery] is run.
-///
-/// The `results` argument contains the provisional results of the query (after
-/// [FakeQuery.limit] and [FakeQuery.offset] have been applied). Callers can
-/// affect the results of the query by returning a different set of results
-/// from the callback.
-///
-/// The callback must not return null.
-typedef QueryCallback<T extends Model<dynamic>> = Iterable<T> Function(Iterable<T> results);
-
-/// Signature for a callback function that will be notified whenever `commit()`
-/// is called, either via [FakeDatastoreDB.commit] or [FakeTransaction.commit].
-///
-/// The `inserts` and `deletes` arguments represent the prospective mutations.
-/// Both arguments are immutable.
-///
-/// This callback will be invoked before any mutations are applied, so by
-/// throwing an exception, callbacks can simulate a failed commit.
-typedef CommitCallback = void Function(List<Model<dynamic>> inserts, List<Key<dynamic>> deletes);
-
-/// A fake datastore database implementation.
-///
-/// This datastore's contents are stored in a single [values] map. Callers can
-/// set up the map to populate the datastore in a way that works for their
-/// test.
-class FakeDatastoreDB implements DatastoreDB {
- FakeDatastoreDB({
- Map<Key<dynamic>, Model<dynamic>>? values,
- Map<Type, QueryCallback<Model<dynamic>>>? onQuery,
- this.onCommit,
- this.commitException = false,
- }) : values = values ?? <Key<dynamic>, Model<dynamic>>{},
- onQuery = onQuery ?? <Type, QueryCallback<Model<dynamic>>>{};
-
- final Map<Key<dynamic>, Model<dynamic>> values;
- final Map<Type, QueryCallback<Model<dynamic>>> onQuery;
- CommitCallback? onCommit;
- // Flag used in tests whether the transaction commit throws exception.
- bool? commitException;
-
- /// Adds a [QueryCallback] to the set of callbacks that will be notified when
- /// queries are run.
- ///
- /// The [callback] argument will replace any existing callback that has been
- /// specified for type `T`, as only one callback may exist per type.
- void addOnQuery<T extends Model<dynamic>>(QueryCallback<T> callback) {
- onQuery[T] = (Iterable<Model<dynamic>> results) {
- return callback(results.cast<T>()).cast<Model<dynamic>>();
- };
- }
-
- @override
- Future<dynamic> commit({List<Model<dynamic>>? inserts, List<Key<dynamic>>? deletes}) async {
- inserts ??= <Model<dynamic>>[];
- deletes ??= <Key<dynamic>>[];
- if (onCommit != null) {
- onCommit!(List<Model<dynamic>>.unmodifiable(inserts), List<Key<dynamic>>.unmodifiable(deletes));
- }
- deletes.forEach(values.remove);
- for (Model<dynamic> model in inserts) {
- values[model.key] = model;
- }
- }
-
- @override
- Datastore get datastore => throw UnimplementedError();
-
- @override
- Partition get defaultPartition => Partition(null);
-
- @override
- Key<dynamic> get emptyKey => defaultPartition.emptyKey;
-
- @override
- Future<List<T?>> lookup<T extends Model<dynamic>>(List<Key<dynamic>> keys) async {
- final List<T?> found = <T?>[];
- for (Key<dynamic> key in keys) {
- for (Model<dynamic> model in values.values) {
- if (model.key.id == key.id) {
- found.add(model as T?);
- }
- }
-
- if (found.isEmpty) {
- throw KeyNotFoundException(key);
- }
- }
-
- return found;
- }
-
- @override
- Future<T> lookupValue<T extends Model<dynamic>>(Key<dynamic> key, {T Function()? orElse}) async {
- final List<T?> values = await lookup(<Key<dynamic>>[key]);
- T? value = values.single;
- if (value == null) {
- if (orElse != null) {
- value = orElse();
- } else {
- throw KeyNotFoundException(key);
- }
- }
- return value;
- }
-
- @override
- ModelDB get modelDB => throw UnimplementedError();
-
- @override
- Partition newPartition(String namespace) => Partition(namespace);
-
- @override
- FakeQuery<T> query<T extends Model<dynamic>>({Partition? partition, Key<dynamic>? ancestorKey}) {
- List<T> results = values.values.whereType<T>().toList();
- if (ancestorKey != null) {
- results = results.where((T entity) => entity.parentKey == ancestorKey).toList();
- }
- return FakeQuery<T>._(this, results);
- }
-
- @override
- Future<T> withTransaction<T>(TransactionHandler<T> transactionHandler) {
- final FakeTransaction transaction = FakeTransaction._(this);
- return transactionHandler(transaction);
- }
-
- @override
- Future<T> lookupOrNull<T extends Model<dynamic>>(Key<dynamic> key) {
- throw UnimplementedError();
- }
-}
-
-/// A query that will return all values of type `T` that exist in the
-/// [FakeDatastoreDB.values] map.
-///
-/// This fake query respects [order], [limit], and [offset]. However, [filter]
-/// may require local additions here to respect new filters.
-class FakeQuery<T extends Model<dynamic>> implements Query<T> {
- FakeQuery._(this.db, this.results);
-
- final FakeDatastoreDB db;
- final List<FakeFilterSpec> filters = <FakeFilterSpec>[];
- final List<FakeOrderSpec> orders = <FakeOrderSpec>[];
-
- List<T> results;
- int start = 0;
- int count = 100;
-
- @override
- void filter(String filterString, Object? comparisonObject) {
- // In production, Datastore filters cannot have a space at the end.
- assert(filterString.trim() == filterString);
- filters.add(FakeFilterSpec._(filterString, comparisonObject));
- }
-
- @override
- void limit(int limit) {
- assert(limit >= 1);
- count = limit;
- }
-
- @override
- void offset(int offset) {
- assert(offset >= 0);
- start = offset;
- }
-
- @override
- void order(String orderString) {
- if (orderString.startsWith('-')) {
- orders.add(FakeOrderSpec._(orderString.substring(1), OrderDirection.Decending));
- } else {
- orders.add(FakeOrderSpec._(orderString, OrderDirection.Ascending));
- }
- }
-
- @override
- Stream<T> run() {
- Iterable<T> resultsView = results;
-
- for (FakeFilterSpec filter in filters) {
- final String filterString = filter.filterString;
- final Object? value = filter.comparisonObject;
- if (filterString.contains('branch =') ||
- filterString.contains('head =') ||
- filterString.contains('pr =') ||
- filterString.contains('repository =') ||
- filterString.contains('name =')) {
- resultsView = resultsView.where((T result) => result.toString().contains(value.toString()));
- }
- }
- resultsView = resultsView.skip(start).take(count);
-
- if (db.onQuery.containsKey(T)) {
- resultsView = db.onQuery[T]!(resultsView).cast<T>();
- }
- return Stream<T>.fromIterable(resultsView);
- }
-}
-
-class FakeFilterSpec {
- const FakeFilterSpec._(this.filterString, this.comparisonObject);
-
- final String filterString;
- final Object? comparisonObject;
-
- @override
- String toString() => 'FakeFilterSpec($filterString, $comparisonObject)';
-}
-
-class FakeOrderSpec {
- const FakeOrderSpec._(this.fieldName, this.direction);
-
- final String fieldName;
- final OrderDirection direction;
-}
-
-/// A fake datastore transaction.
-///
-/// This class keeps track of [inserts] and [deletes] and updates the parent
-/// [FakeDatastoreDB] when the transaction is committed.
-class FakeTransaction implements Transaction {
- FakeTransaction._(this.db);
-
- final Map<Key<dynamic>, Model<dynamic>> inserts = <Key<dynamic>, Model<dynamic>>{};
- final Set<Key<dynamic>> deletes = <Key<dynamic>>{};
- bool sealed = false;
-
- @override
- final FakeDatastoreDB db;
-
- @override
- Future<dynamic> commit() async {
- if (db.commitException!) {
- throw DatastoreError();
- }
- if (sealed) {
- throw StateError('Transaction sealed');
- }
- if (db.onCommit != null) {
- db.onCommit!(List<Model<dynamic>>.unmodifiable(inserts.values), List<Key<dynamic>>.unmodifiable(deletes));
- }
- for (MapEntry<Key<dynamic>, Model<dynamic>> entry in inserts.entries) {
- db.values[entry.key] = entry.value;
- }
- deletes.forEach(db.values.remove);
- sealed = true;
- }
-
- @override
- Future<List<T>> lookup<T extends Model<dynamic>>(List<Key<dynamic>> keys) async {
- final List<T> results = <T>[];
- for (Key<dynamic> key in keys) {
- if (deletes.contains(key)) {
- // results.add(null);
- } else if (inserts.containsKey(key)) {
- results.add(inserts[key] as T);
- } else if (db.values.containsKey(key)) {
- results.add(db.values[key] as T);
- } else {
- // results.add(null);
- }
- }
- return results;
- }
-
- @override
- Future<T> lookupValue<T extends Model<dynamic>>(Key<dynamic> key, {T Function()? orElse}) async {
- final List<T?> values = await lookup(<Key<dynamic>>[key]);
- T? value = values.single;
- if (value == null) {
- if (orElse != null) {
- value = orElse();
- } else {
- throw KeyNotFoundException(key);
- }
- }
- return value;
- }
-
- @override
- Query<T> query<T extends Model<dynamic>>(Key<dynamic> ancestorKey, {Partition? partition}) {
- final List<T> queryResults = <T>[
- ...inserts.values.whereType<T>(),
- ...db.values.values.whereType<T>(),
- ];
- deletes.whereType<T>().forEach(queryResults.remove);
- return FakeQuery<T>._(db, queryResults);
- }
-
- @override
- void queueMutations({List<Model<dynamic>>? inserts, List<Key<dynamic>>? deletes}) {
- if (sealed) {
- throw StateError('Transaction sealed');
- }
- if (inserts != null) {
- final math.Random random = math.Random();
- for (Model<dynamic> insert in inserts) {
- Key<dynamic> key = insert.key;
- if (key.id == null) {
- key = Key<dynamic>(key.parent!, key.type, random.nextInt(math.pow(2, 20).toInt()));
- }
- this.inserts[key] = insert;
- }
- }
- if (deletes != null) {
- this.deletes.addAll(deletes);
- }
- }
-
- @override
- Future<dynamic> rollback() async {
- if (sealed) {
- throw StateError('Transaction sealed');
- }
- inserts.clear();
- deletes.clear();
- sealed = true;
- }
-
- @override
- Future<T> lookupOrNull<T extends Model<dynamic>>(Key<dynamic> key) {
- throw UnimplementedError();
- }
-}
diff --git a/app_dart/test/src/request_handling/api_request_handler_tester.dart b/app_dart/test/src/request_handling/api_request_handler_tester.dart
deleted file mode 100644
index 9c8d657..0000000
--- a/app_dart/test/src/request_handling/api_request_handler_tester.dart
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-
-import 'package:cocoon_service/src/request_handling/api_request_handler.dart';
-import 'package:cocoon_service/src/request_handling/body.dart';
-import 'package:cocoon_service/src/request_handling/request_handler.dart';
-import 'package:meta/meta.dart';
-
-import 'fake_authentication.dart';
-import 'request_handler_tester.dart';
-
-class ApiRequestHandlerTester extends RequestHandlerTester {
- ApiRequestHandlerTester({
- super.request,
- FakeAuthenticatedContext? context,
- Map<String, dynamic>? requestData,
- }) : context = context ?? FakeAuthenticatedContext(),
- requestData = requestData ?? <String, dynamic>{};
-
- FakeAuthenticatedContext context;
- Map<String, dynamic> requestData;
-
- @override
- @protected
- Future<T> run<T extends Body>(Future<T> Function() callback) {
- return super.run<T>(() {
- return runZoned<Future<T>>(
- () {
- return callback();
- },
- zoneValues: <RequestKey<dynamic>, Object>{
- ApiKey.authContext: context,
- ApiKey.requestData: requestData,
- },
- );
- });
- }
-}
diff --git a/app_dart/test/src/request_handling/fake_authentication.dart b/app_dart/test/src/request_handling/fake_authentication.dart
deleted file mode 100644
index 312d20b..0000000
--- a/app_dart/test/src/request_handling/fake_authentication.dart
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright 2019 The Flutter 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:appengine/appengine.dart';
-import 'package:cocoon_service/src/foundation/typedefs.dart';
-import 'package:cocoon_service/src/model/appengine/key_helper.dart';
-import 'package:cocoon_service/src/model/google/token_info.dart';
-import 'package:cocoon_service/src/request_handling/authentication.dart';
-import 'package:cocoon_service/src/request_handling/exceptions.dart';
-import 'package:cocoon_service/src/service/config.dart';
-
-// ignore: must_be_immutable
-class FakeAuthenticationProvider implements AuthenticationProvider {
- FakeAuthenticationProvider({
- FakeClientContext? clientContext,
- this.authenticated = true,
- }) : clientContext = clientContext ?? FakeClientContext();
-
- bool authenticated;
- FakeClientContext clientContext;
-
- @override
- Future<AuthenticatedContext> authenticate(HttpRequest request) async {
- if (authenticated) {
- return FakeAuthenticatedContext(clientContext: clientContext);
- } else {
- throw const Unauthenticated('Not authenticated');
- }
- }
-
- @override
- Future<AuthenticatedContext> authenticateToken(TokenInfo token, {ClientContext? clientContext, Logging? log}) async {
- if (authenticated) {
- return FakeAuthenticatedContext(clientContext: clientContext as FakeClientContext?);
- } else {
- throw const Unauthenticated('Not authenticated');
- }
- }
-
- @override
- ClientContextProvider get clientContextProvider => throw UnimplementedError();
-
- @override
- Config get config => throw UnimplementedError();
-
- @override
- HttpClientProvider get httpClientProvider => throw UnimplementedError();
-
- @override
- Future<TokenInfo> tokenInfo(HttpRequest request, {Logging? log, String tokenType = 'id_token'}) async {
- return TokenInfo(
- email: 'abc@gmail.com',
- issued: DateTime.now(),
- );
- }
-}
-
-// ignore: must_be_immutable
-class FakeAuthenticatedContext implements AuthenticatedContext {
- FakeAuthenticatedContext({
- FakeClientContext? clientContext,
- }) : clientContext = clientContext ?? FakeClientContext();
-
- @override
- FakeClientContext clientContext;
-}
-
-class FakeClientContext implements ClientContext {
- FakeClientContext({
- this.isDevelopmentEnvironment = true,
- this.isProductionEnvironment = false,
- FakeAppEngineContext? applicationContext,
- }) : applicationContext = applicationContext ?? FakeAppEngineContext();
-
- @override
- FakeAppEngineContext applicationContext;
-
- @override
- bool isDevelopmentEnvironment;
-
- @override
- bool isProductionEnvironment;
-
- @override
- late Services services;
-
- @override
- String? traceId;
-}
-
-class FakeAppEngineContext implements AppEngineContext {
- @override
- String applicationID = 'flutter-dashboard';
-
- @override
- late String fullQualifiedApplicationId;
-
- @override
- late String instance;
-
- @override
- String? instanceId;
-
- @override
- late bool isDevelopmentEnvironment;
-
- @override
- late String module;
-
- @override
- String partition = '[default]';
-
- @override
- late String version;
-}
-
-class FakeKeyHelper extends KeyHelper {
- FakeKeyHelper({
- AppEngineContext? applicationContext,
- }) : super(applicationContext: applicationContext ?? FakeAppEngineContext());
-}
diff --git a/app_dart/test/src/request_handling/fake_http.dart b/app_dart/test/src/request_handling/fake_http.dart
deleted file mode 100644
index 7dc3785..0000000
--- a/app_dart/test/src/request_handling/fake_http.dart
+++ /dev/null
@@ -1,668 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-import 'dart:convert';
-import 'dart:io';
-import 'dart:typed_data';
-
-import 'package:meta/meta.dart';
-
-typedef ContentLengthProvider = int Function();
-
-@immutable
-class _Body {
- _Body.empty()
- : isUtf8 = true,
- value = null,
- bytes = Uint8List(0),
- stream = Stream<Uint8List>.fromIterable(const Iterable<Uint8List>.empty());
-
- _Body.utf8(String this.value)
- : isUtf8 = true,
- bytes = utf8.encode(value),
- stream = Stream<Uint8List>.fromIterable(<Uint8List>[utf8.encode(value)]);
-
- _Body.rawBytes(this.bytes)
- : isUtf8 = false,
- value = null,
- stream = Stream<Uint8List>.fromIterable(<Uint8List>[bytes]);
-
- _Body.copy(_Body other)
- : isUtf8 = other.isUtf8,
- value = other.value,
- bytes = other.bytes,
- stream = Stream<Uint8List>.fromIterable(<Uint8List>[other.bytes]);
-
- final bool isUtf8;
- final String? value;
- final Uint8List bytes;
- final Stream<Uint8List> stream;
-}
-
-abstract class FakeTransport {
- int get contentLength;
-
- HttpConnectionInfo? get connectionInfo => null;
-
- final List<FakeCookie> cookies = <FakeCookie>[];
-
- FakeHttpHeaders get headers {
- _headers ??= FakeHttpHeaders(contentLengthProvider: () => contentLength);
- return _headers!;
- }
-
- FakeHttpHeaders? _headers;
-
- bool get persistentConnection => false;
-}
-
-// TODO(tvolkert): `implements Stream<Uint8List>` once HttpClientResponse does the same
-abstract class FakeInbound extends FakeTransport {
- FakeInbound(String? body) : _body = body == null ? _Body.empty() : _Body.utf8(body);
-
- /// Indicates whether the body stream has been exposed to callers in any way.
- /// Once the body stream has been exposed to callers, [body] becomes
- /// immutable.
- bool _isStreamExposed = false;
-
- /// Resets this transport so that it may be reused.
- @mustCallSuper
- void reset() {
- _body = _Body.copy(_body);
- _isStreamExposed = false;
- }
-
- /// The UTF-8 encoded value of the HTTP request body, or null if this request
- /// specifies no body.
- ///
- /// If the HTTP request body was set via [bodyBytes], then it's assumed that
- /// the body is not a UTF-8 encoded string, and subsequently attempting to
- /// access [body] will throw a [StateError].
- ///
- /// Once the body stream has been exposed to callers in any way, the [body]
- /// value becomes immutable (as does the [bodyBytes] value), and any attempt
- /// to modify it will throw a [StateError].
- String? get body {
- if (!_body.isUtf8) {
- throw StateError('body is not a valid UTF-8 string');
- }
- return _body.value;
- }
-
- _Body _body;
- set body(String? value) {
- if (_isStreamExposed) {
- throw StateError('The body of this transport has been made immutable');
- }
- _body = value == null ? _Body.empty() : _Body.utf8(value);
- }
-
- /// The raw bytes of the HTTP request body.
- ///
- /// This will never be null; if the HTTP request body is empty, this will be
- /// the empty list.
- ///
- /// Setting this value directly will be assumed to be because the bytes are
- /// not a UTF-8 encoded string, and subsequently attempting to access [body]
- /// will throw a [StateError].
- ///
- /// Once the body stream has been exposed to callers in any way, the
- /// [bodyBytes] value becomes immutable (as does the [body] value), and any
- /// attempt to modify it will throw a [StateError].
- Uint8List get bodyBytes => _body.bytes;
- set bodyBytes(Uint8List value) {
- if (_isStreamExposed) {
- throw StateError('The body of this transport has been made immutable');
- }
- _body = _Body.rawBytes(value);
- }
-
- StreamSubscription<Uint8List> listen(
- void Function(Uint8List event)? onData, {
- Function? onError,
- void Function()? onDone,
- bool? cancelOnError,
- }) {
- _isStreamExposed = true;
- return _body.stream.listen(
- onData,
- onError: onError,
- onDone: onDone,
- cancelOnError: cancelOnError,
- );
- }
-
- Future<bool> any(bool Function(Uint8List element) test) {
- _isStreamExposed = true;
- return _body.stream.any(test);
- }
-
- Stream<Uint8List> asBroadcastStream({
- void Function(StreamSubscription<Uint8List> subscription)? onListen,
- void Function(StreamSubscription<Uint8List> subscription)? onCancel,
- }) {
- _isStreamExposed = true;
- return _body.stream.asBroadcastStream(onListen: onListen, onCancel: onCancel);
- }
-
- Stream<E> asyncExpand<E>(Stream<E>? Function(Uint8List event) convert) {
- _isStreamExposed = true;
- return _body.stream.asyncExpand<E>(convert);
- }
-
- Stream<E> asyncMap<E>(FutureOr<E> Function(Uint8List event) convert) {
- _isStreamExposed = true;
- return _body.stream.asyncMap<E>(convert);
- }
-
- Stream<R> cast<R>() {
- _isStreamExposed = true;
- return _body.stream.cast<R>();
- }
-
- Future<bool> contains(Object? needle) {
- _isStreamExposed = true;
- return _body.stream.contains(needle);
- }
-
- Stream<Uint8List> distinct([bool Function(Uint8List previous, Uint8List next)? equals]) {
- _isStreamExposed = true;
- return _body.stream.distinct(equals);
- }
-
- Future<E> drain<E>([E? futureValue]) {
- _isStreamExposed = true;
- return _body.stream.drain<E>(futureValue);
- }
-
- Future<Uint8List> elementAt(int index) {
- _isStreamExposed = true;
- return _body.stream.elementAt(index);
- }
-
- Future<bool> every(bool Function(Uint8List element) test) {
- _isStreamExposed = true;
- return _body.stream.every(test);
- }
-
- Stream<S> expand<S>(Iterable<S> Function(Uint8List element) convert) {
- _isStreamExposed = true;
- return _body.stream.expand(convert);
- }
-
- Future<Uint8List> get first {
- _isStreamExposed = true;
- return _body.stream.first;
- }
-
- Future<Uint8List> firstWhere(
- bool Function(Uint8List element) test, {
- List<int> Function()? orElse,
- }) {
- _isStreamExposed = true;
- return _body.stream.firstWhere(test, orElse: () => Uint8List.fromList(orElse!()));
- }
-
- Future<S> fold<S>(S initialValue, S Function(S previous, Uint8List element) combine) {
- _isStreamExposed = true;
- return _body.stream.fold<S>(initialValue, combine);
- }
-
- Future<dynamic> forEach(void Function(Uint8List element) action) {
- _isStreamExposed = true;
- return _body.stream.forEach(action);
- }
-
- Stream<Uint8List> handleError(
- Function onError, {
- bool Function(dynamic error)? test,
- }) {
- _isStreamExposed = true;
- return _body.stream.handleError(onError, test: test);
- }
-
- bool get isBroadcast {
- _isStreamExposed = true;
- return _body.stream.isBroadcast;
- }
-
- Future<bool> get isEmpty {
- _isStreamExposed = true;
- return _body.stream.isEmpty;
- }
-
- Future<String> join([String separator = '']) {
- _isStreamExposed = true;
- return _body.stream.join(separator);
- }
-
- Future<Uint8List> get last {
- _isStreamExposed = true;
- return _body.stream.last;
- }
-
- Future<Uint8List> lastWhere(
- bool Function(Uint8List element) test, {
- Uint8List Function()? orElse,
- }) {
- _isStreamExposed = true;
- return _body.stream.lastWhere(test, orElse: () => Uint8List.fromList(orElse!()));
- }
-
- Future<int> get length {
- _isStreamExposed = true;
- return _body.stream.length;
- }
-
- Stream<S> map<S>(S Function(Uint8List event) convert) {
- _isStreamExposed = true;
- return _body.stream.map<S>(convert);
- }
-
- Future<dynamic> pipe(StreamConsumer<Uint8List> streamConsumer) {
- _isStreamExposed = true;
- return _body.stream.map((Uint8List list) => list.toList()).pipe(streamConsumer);
- }
-
- Future<Uint8List> reduce(List<int> Function(Uint8List previous, Uint8List element) combine) {
- _isStreamExposed = true;
- return _body.stream
- .reduce((Uint8List previous, Uint8List element) => Uint8List.fromList(combine(previous, element)));
- }
-
- Future<Uint8List> get single {
- _isStreamExposed = true;
- return _body.stream.single;
- }
-
- Future<Uint8List> singleWhere(
- bool Function(Uint8List element) test, {
- List<int> Function()? orElse,
- }) {
- _isStreamExposed = true;
- return _body.stream.singleWhere(test, orElse: () => Uint8List.fromList(orElse!()));
- }
-
- Stream<Uint8List> skip(int count) {
- _isStreamExposed = true;
- return _body.stream.skip(count);
- }
-
- Stream<Uint8List> skipWhile(bool Function(Uint8List element) test) {
- _isStreamExposed = true;
- return _body.stream.skipWhile(test);
- }
-
- Stream<Uint8List> take(int count) {
- _isStreamExposed = true;
- return _body.stream.take(count);
- }
-
- Stream<Uint8List> takeWhile(bool Function(Uint8List element) test) {
- _isStreamExposed = true;
- return _body.stream.takeWhile(test);
- }
-
- Stream<Uint8List> timeout(
- Duration timeLimit, {
- void Function(EventSink<Uint8List> sink)? onTimeout,
- }) {
- _isStreamExposed = true;
- return _body.stream.timeout(timeLimit, onTimeout: onTimeout);
- }
-
- Future<List<Uint8List>> toList() {
- _isStreamExposed = true;
- return _body.stream.toList();
- }
-
- Future<Set<Uint8List>> toSet() {
- _isStreamExposed = true;
- return _body.stream.toSet();
- }
-
- Stream<S> transform<S>(StreamTransformer<List<int>, S> streamTransformer) {
- _isStreamExposed = true;
- return _body.stream.map((Uint8List list) => list.toList()).transform<S>(streamTransformer);
- }
-
- Stream<Uint8List> where(bool Function(Uint8List event) test) {
- _isStreamExposed = true;
- return _body.stream.where(test);
- }
-
- @override
- int get contentLength => _body.bytes.length;
-
- X509Certificate? get certificate => null;
-}
-
-abstract class FakeOutbound extends FakeTransport implements IOSink {
- StringBuffer _buffer = StringBuffer();
-
- String get body => _buffer.toString();
-
- List<Object> get errors => _errors;
- List<Object> _errors = <Object>[];
-
- /// Whether this outbound has been closed.
- bool get isClosed => _isClosed;
- bool _isClosed = false;
-
- /// Resets this transport so that it may be reused.
- @mustCallSuper
- void reset() {
- _isClosed = false;
- _buffer = StringBuffer();
- _errors = <Object>[];
- }
-
- @override
- Encoding get encoding => utf8;
-
- @override
- set encoding(Encoding value) => throw UnsupportedError('Unsupported');
-
- @override
- void add(List<int> data) {
- if (isClosed) {
- throw StateError('Transport is closed');
- }
- headers._sealed = true;
- _buffer.write(utf8.decode(data));
- }
-
- @override
- void addError(Object error, [StackTrace? stackTrace]) {
- if (isClosed) {
- throw StateError('Transport is closed');
- }
- errors.add(error);
- }
-
- @override
- Future<dynamic> addStream(Stream<List<int>> stream) async {
- if (isClosed) {
- throw StateError('Transport is closed');
- }
- headers._sealed = true;
- _buffer.write(await utf8.decoder.bind(stream).join());
- }
-
- @override
- void write(Object? obj) {
- if (isClosed) {
- throw StateError('Transport is closed');
- }
- headers._sealed = true;
- _buffer.write(obj);
- }
-
- @override
- void writeAll(Iterable<dynamic> objects, [String separator = '']) {
- if (isClosed) {
- throw StateError('Transport is closed');
- }
- headers._sealed = true;
- _buffer.writeAll(objects, separator);
- }
-
- @override
- void writeCharCode(int charCode) {
- if (isClosed) {
- throw StateError('Transport is closed');
- }
- headers._sealed = true;
- _buffer.writeCharCode(charCode);
- }
-
- @override
- void writeln([Object? obj = '']) {
- if (isClosed) {
- throw StateError('Transport is closed');
- }
- headers._sealed = true;
- _buffer.writeln(obj);
- }
-
- @override
- Future<dynamic> get done async {}
-
- @override
- Future<dynamic> flush() async {}
-
- @override
- Future<dynamic> close() async {
- _isClosed = true;
- }
-
- bool get bufferOutput => false;
-
- set bufferOutput(bool value) => throw UnsupportedError('Unsupported');
-
- @override
- int get contentLength => _contentLength ?? _buffer.length;
- int? _contentLength;
-
- set contentLength(int value) {
- _contentLength = value;
- }
-
- set persistentConnection(bool value) => throw UnsupportedError('Unsupported');
-}
-
-abstract class FakeCookie implements Cookie {
- FakeCookie({
- required this.name,
- required this.value,
- this.domain,
- this.path,
- this.expires,
- required this.httpOnly,
- this.maxAge,
- required this.secure,
- });
-
- @override
- String name;
-
- @override
- String value;
-
- @override
- String? domain;
-
- @override
- String? path;
-
- @override
- DateTime? expires;
-
- @override
- bool httpOnly;
-
- @override
- int? maxAge;
-
- @override
- bool secure;
-}
-
-class FakeHttpHeaders implements HttpHeaders {
- FakeHttpHeaders({this.contentLengthProvider});
-
- final ContentLengthProvider? contentLengthProvider;
- final Map<String, List<String>> _values = <String, List<String>>{};
- bool _sealed = false;
-
- void _checkSealed() {
- if (_sealed) {
- throw StateError('HTTP headers are sealed');
- }
- }
-
- @override
- bool get chunkedTransferEncoding => false;
-
- @override
- set chunkedTransferEncoding(bool value) => throw UnsupportedError('Unsupported');
-
- @override
- int get contentLength => contentLengthProvider != null ? contentLengthProvider!() : -1;
-
- @override
- set contentLength(int value) => throw UnsupportedError('Unsupported');
-
- @override
- ContentType get contentType => throw UnimplementedError();
-
- @override
- set contentType(ContentType? value) {
- _checkSealed();
- removeAll(HttpHeaders.contentTypeHeader);
- add(HttpHeaders.contentTypeHeader, '$value');
- }
-
- @override
- DateTime? date;
-
- @override
- DateTime? expires;
-
- @override
- String? host;
-
- @override
- DateTime? ifModifiedSince;
-
- @override
- late bool persistentConnection;
-
- @override
- int? port;
-
- @override
- List<String>? operator [](String name) => _values[name];
-
- @override
- void add(String name, Object value, {bool preserveHeaderCase = false}) {
- _checkSealed();
- name = name.toLowerCase();
- _values[name] ??= <String>[];
- _values[name]!.add('$value');
- }
-
- @override
- void clear() {
- _checkSealed();
- _values.clear();
- }
-
- @override
- void forEach(void Function(String name, List<String> values) f) {
- _values.forEach(f);
- }
-
- @override
- void noFolding(String name) {}
-
- @override
- void remove(String name, Object value) {
- _checkSealed();
- name = name.toLowerCase();
- if (_values.containsKey('$value')) {
- _values[name]!.remove('$value');
- }
- }
-
- @override
- void removeAll(String name) {
- _checkSealed();
- name = name.toLowerCase();
- _values.remove(name);
- }
-
- @override
- void set(String name, Object value, {bool preserveHeaderCase = false}) {
- _checkSealed();
- name = name.toLowerCase();
- _values[name] = <String>['$value'];
- }
-
- @override
- String? value(String name) {
- final List<String>? value = _values[name.toLowerCase()];
- return value?.single;
- }
-}
-
-class FakeHttpRequest extends FakeInbound implements HttpRequest {
- /// Creates a new [FakeHttpRequest].
- ///
- /// If the optional [body] argument is specified, the request stream will
- /// yield the specified body value when UTF-8 decoded. By default, the
- /// request stream will be empty. The [body] property can be modified until
- /// the stream has been exposed to callers, at which time it becomes
- /// immutable.
- FakeHttpRequest({
- this.method = 'GET',
- String? body,
- String path = '/',
- Map<String, dynamic>? queryParametersValue,
- FakeHttpResponse? response,
- }) : uri = Uri(path: path, queryParameters: queryParametersValue),
- response = response ?? FakeHttpResponse(),
- super(body);
-
- @override
- String method;
-
- @override
- Uri uri;
-
- String get path => uri.path;
- set path(String value) {
- uri = uri.replace(path: value);
- }
-
- @override
- FakeHttpResponse response;
-
- @override
- String get protocolVersion => '1.1';
-
- @override
- Uri get requestedUri => uri;
-
- @override
- HttpSession get session => throw UnsupportedError('Unsupported');
-}
-
-class FakeHttpResponse extends FakeOutbound implements HttpResponse {
- @override
- Duration? get deadline => null;
-
- @override
- set deadline(Duration? value) => throw UnsupportedError('Unsupported');
-
- @override
- String get reasonPhrase => 'reason';
-
- @override
- set reasonPhrase(String value) => throw UnsupportedError('Unsupported');
-
- @override
- int statusCode = HttpStatus.ok;
-
- @override
- Future<dynamic> redirect(Uri location, {int status = HttpStatus.movedTemporarily}) {
- statusCode = status;
- headers.add(HttpHeaders.locationHeader, '$location');
- return close();
- }
-
- @override
- Future<Socket> detachSocket({bool writeHeaders = true}) => throw UnsupportedError('Unsupported');
-}
diff --git a/app_dart/test/src/request_handling/fake_pubsub.dart b/app_dart/test/src/request_handling/fake_pubsub.dart
deleted file mode 100644
index 1810a74..0000000
--- a/app_dart/test/src/request_handling/fake_pubsub.dart
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:cocoon_service/src/request_handling/pubsub.dart';
-import 'package:googleapis/pubsub/v1.dart';
-
-class FakePubSub extends PubSub {
- List<dynamic> messages = <dynamic>[];
- bool exceptionFlag = false;
- int exceptionRepetition = 1;
-
- @override
- Future<List<String>> publish(String topicName, dynamic json) async {
- if (exceptionFlag && exceptionRepetition > 0) {
- exceptionRepetition--;
- throw DetailedApiRequestError(500, 'test api error');
- }
- messages.add(json);
- return <String>[];
- }
-}
diff --git a/app_dart/test/src/request_handling/fake_request_handler.dart b/app_dart/test/src/request_handling/fake_request_handler.dart
deleted file mode 100644
index 4a4c19a..0000000
--- a/app_dart/test/src/request_handling/fake_request_handler.dart
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:cocoon_service/src/request_handling/body.dart';
-import 'package:cocoon_service/src/request_handling/request_handler.dart';
-
-// ignore: must_be_immutable
-class FakeRequestHandler extends RequestHandler<Body> {
- FakeRequestHandler({
- required this.body,
- required super.config,
- });
-
- final Body body;
-
- int callCount = 0;
-
- @override
- Future<Body> get() async {
- callCount++;
- return body;
- }
-
- @override
- Future<Body> post() async {
- return body;
- }
-}
diff --git a/app_dart/test/src/request_handling/no_auth_request_handler_tester.dart b/app_dart/test/src/request_handling/no_auth_request_handler_tester.dart
deleted file mode 100644
index 167ca66..0000000
--- a/app_dart/test/src/request_handling/no_auth_request_handler_tester.dart
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-
-import 'package:cocoon_service/src/request_handling/body.dart';
-import 'package:cocoon_service/src/request_handling/no_auth_request_handler.dart';
-import 'package:cocoon_service/src/request_handling/request_handler.dart';
-import 'package:meta/meta.dart';
-
-import 'request_handler_tester.dart';
-
-class NoAuthRequestHandlerTester extends RequestHandlerTester {
- NoAuthRequestHandlerTester({
- super.request,
- Map<String, dynamic>? requestData,
- }) : requestData = requestData ?? <String, dynamic>{};
-
- Map<String, dynamic> requestData;
-
- @override
- @protected
- Future<T> run<T extends Body>(Future<T> Function() callback) {
- return super.run<T>(() {
- return runZoned<Future<T>>(
- () {
- return callback();
- },
- zoneValues: <RequestKey<dynamic>, Object>{
- NoAuthKey.requestData: requestData,
- },
- );
- });
- }
-}
diff --git a/app_dart/test/src/request_handling/request_handler_tester.dart b/app_dart/test/src/request_handling/request_handler_tester.dart
deleted file mode 100644
index 956b2a4..0000000
--- a/app_dart/test/src/request_handling/request_handler_tester.dart
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-
-import 'package:cocoon_service/src/request_handling/body.dart';
-import 'package:cocoon_service/src/request_handling/request_handler.dart';
-import 'package:http/testing.dart' as http;
-import 'package:meta/meta.dart';
-
-import 'fake_http.dart';
-
-class RequestHandlerTester {
- RequestHandlerTester({
- FakeHttpRequest? request,
- this.httpClient,
- }) {
- this.request = request ?? FakeHttpRequest();
- }
-
- FakeHttpRequest? request;
- http.MockClient? httpClient;
-
- /// This tester's [FakeHttpResponse], derived from [request].
- FakeHttpResponse get response => request!.response;
-
- /// Executes [RequestHandler.get] on the specified [handler].
- Future<T> get<T extends Body>(RequestHandler<T> handler) {
- return run<T>(() {
- return handler.get(); // ignore: invalid_use_of_protected_member
- });
- }
-
- /// Executes [RequestHandler.post] on the specified [handler].
- Future<T> post<T extends Body>(RequestHandler<T> handler) {
- return run<T>(() {
- return handler.post(); // ignore: invalid_use_of_protected_member
- });
- }
-
- @protected
- Future<T> run<T extends Body>(Future<T> Function() callback) {
- return runZoned<Future<T>>(
- () {
- return callback();
- },
- zoneValues: <RequestKey<dynamic>, Object?>{
- RequestKey.request: request,
- RequestKey.response: response,
- RequestKey.httpClient: httpClient,
- },
- );
- }
-}
diff --git a/app_dart/test/src/request_handling/subscription_tester.dart b/app_dart/test/src/request_handling/subscription_tester.dart
deleted file mode 100644
index d6cf64f..0000000
--- a/app_dart/test/src/request_handling/subscription_tester.dart
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-
-import 'package:cocoon_service/src/model/luci/push_message.dart';
-import 'package:cocoon_service/src/request_handling/api_request_handler.dart';
-import 'package:cocoon_service/src/request_handling/body.dart';
-import 'package:cocoon_service/src/request_handling/request_handler.dart';
-import 'package:cocoon_service/src/request_handling/subscription_handler.dart';
-import 'package:meta/meta.dart';
-
-import 'fake_authentication.dart';
-import 'request_handler_tester.dart';
-
-class SubscriptionTester extends RequestHandlerTester {
- SubscriptionTester({
- super.request,
- FakeAuthenticatedContext? context,
- this.message = const PushMessage(),
- }) : context = context ?? FakeAuthenticatedContext();
-
- FakeAuthenticatedContext context;
- PushMessage message;
-
- @override
- @protected
- Future<T> run<T extends Body>(Future<T> Function() callback) {
- return super.run<T>(() {
- return runZoned<Future<T>>(
- () {
- return callback();
- },
- zoneValues: <RequestKey<dynamic>, Object>{
- ApiKey.authContext: context,
- PubSubKey.message: message,
- },
- );
- });
- }
-}
diff --git a/app_dart/test/src/service/fake_auth_client.dart b/app_dart/test/src/service/fake_auth_client.dart
deleted file mode 100644
index 93b502a..0000000
--- a/app_dart/test/src/service/fake_auth_client.dart
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2020 The Flutter 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:convert';
-import 'dart:typed_data';
-
-import 'package:googleapis_auth/googleapis_auth.dart';
-import 'package:http/http.dart' as http;
-
-class FakeAuthClient extends AutoRefreshingAuthClient {
- FakeAuthClient(this.baseClient);
-
- final http.Client baseClient;
-
- @override
- void close() => baseClient.close();
-
- @override
- Stream<AccessCredentials> get credentialUpdates => throw UnimplementedError();
-
- @override
- AccessCredentials get credentials => throw UnimplementedError();
-
- @override
- Future<http.Response> delete(Uri url, {Map<String, String>? headers, Object? body, Encoding? encoding}) async =>
- baseClient.delete(
- url,
- headers: headers,
- encoding: encoding,
- );
- @override
- Future<http.Response> get(Uri url, {Map<String, String>? headers}) async => baseClient.get(url, headers: headers);
-
- @override
- Future<http.Response> head(Uri url, {Map<String, String>? headers}) async => baseClient.head(url, headers: headers);
-
- @override
- Future<http.Response> patch(Uri url, {Map<String, String>? headers, Object? body, Encoding? encoding}) async =>
- baseClient.patch(
- url,
- headers: headers,
- body: body,
- encoding: encoding,
- );
-
- @override
- Future<http.Response> post(Uri url, {Map<String, String>? headers, Object? body, Encoding? encoding}) async =>
- baseClient.post(
- url,
- headers: headers,
- body: body,
- encoding: encoding,
- );
-
- @override
- Future<http.Response> put(Uri url, {Map<String, String>? headers, Object? body, Encoding? encoding}) async =>
- baseClient.put(
- url,
- headers: headers,
- body: body,
- encoding: encoding,
- );
-
- @override
- Future<String> read(Uri url, {Map<String, String>? headers}) async => baseClient.read(url, headers: headers);
-
- @override
- Future<Uint8List> readBytes(Uri url, {Map<String, String>? headers}) async =>
- baseClient.readBytes(url, headers: headers);
-
- @override
- Future<http.StreamedResponse> send(http.BaseRequest request) async => baseClient.send(request);
-}
diff --git a/app_dart/test/src/service/fake_bigquery_service.dart b/app_dart/test/src/service/fake_bigquery_service.dart
deleted file mode 100644
index 7013242..0000000
--- a/app_dart/test/src/service/fake_bigquery_service.dart
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2020 The Flutter 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 'package:cocoon_service/src/service/bigquery.dart';
-import 'package:googleapis/bigquery/v2.dart';
-
-import '../utilities/mocks.dart';
-
-class FakeBigqueryService extends BigqueryService {
- FakeBigqueryService(this.jobsResource) : super(MockAccessClientProvider());
-
- JobsResource jobsResource;
-
- @override
- Future<JobsResource> defaultJobs() async {
- return jobsResource;
- }
-}
diff --git a/app_dart/test/src/service/fake_build_status_provider.dart b/app_dart/test/src/service/fake_build_status_provider.dart
deleted file mode 100644
index 1bc7cab..0000000
--- a/app_dart/test/src/service/fake_build_status_provider.dart
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:cocoon_service/src/service/build_status_provider.dart';
-import 'package:cocoon_service/src/service/datastore.dart';
-import 'package:github/github.dart';
-
-class FakeBuildStatusService implements BuildStatusService {
- FakeBuildStatusService({
- this.cumulativeStatus,
- this.commitStatuses,
- });
-
- BuildStatus? cumulativeStatus;
- List<CommitStatus>? commitStatuses;
-
- @override
- Future<BuildStatus?> calculateCumulativeStatus(RepositorySlug slug) async {
- if (cumulativeStatus == null) {
- throw AssertionError();
- }
- return cumulativeStatus;
- }
-
- @override
- Stream<CommitStatus> retrieveCommitStatus({
- int limit = 100,
- int? timestamp,
- String? branch,
- required RepositorySlug slug,
- }) {
- if (commitStatuses == null) {
- throw AssertionError();
- }
- commitStatuses!.sort((CommitStatus a, CommitStatus b) => a.commit.timestamp!.compareTo(b.commit.timestamp!));
-
- return Stream<CommitStatus>.fromIterable(
- commitStatuses!.where(
- (CommitStatus commitStatus) =>
- ((commitStatus.commit.timestamp == null || timestamp == null)
- ? true
- : commitStatus.commit.timestamp! < timestamp) &&
- commitStatus.commit.branch == branch,
- ),
- );
- }
-
- @override
- DatastoreService get datastoreService => throw UnimplementedError();
-}
diff --git a/app_dart/test/src/service/fake_buildbucket.dart b/app_dart/test/src/service/fake_buildbucket.dart
deleted file mode 100644
index 8d57b99..0000000
--- a/app_dart/test/src/service/fake_buildbucket.dart
+++ /dev/null
@@ -1,150 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:cocoon_service/src/model/luci/buildbucket.dart';
-import 'package:cocoon_service/src/service/buildbucket.dart';
-import 'package:http/http.dart' as http;
-import 'package:http/testing.dart';
-
-/// Fake [BuildBucketClient] for handling requests to BuildBucket.
-///
-/// By default, returns good responses but can be updated to throw exceptions.
-// ignore: must_be_immutable
-class FakeBuildBucketClient extends BuildBucketClient {
- FakeBuildBucketClient({
- this.scheduleBuildResponse,
- this.searchBuildsResponse,
- this.batchResponse,
- this.cancelBuildResponse,
- this.getBuildResponse,
- this.listBuildersResponse,
- }) : super(httpClient: MockClient((_) async => http.Response('', 200)));
-
- Future<Build>? scheduleBuildResponse;
- Future<SearchBuildsResponse>? searchBuildsResponse;
- Future<BatchResponse> Function()? batchResponse;
- Future<Build>? cancelBuildResponse;
- Future<Build>? getBuildResponse;
- Future<ListBuildersResponse>? listBuildersResponse;
-
- @override
- Future<Build> scheduleBuild(
- ScheduleBuildRequest? request, {
- String buildBucketUri = 'https://localhost/builds',
- }) async =>
- (scheduleBuildResponse != null)
- ? await scheduleBuildResponse!
- : Build(
- id: '123',
- builderId: request!.builderId,
- tags: request.tags,
- );
-
- @override
- Future<SearchBuildsResponse> searchBuilds(
- SearchBuildsRequest? request, {
- String buildBucketUri = 'https://localhost/builds',
- }) async =>
- (searchBuildsResponse != null)
- ? await searchBuildsResponse!
- : const SearchBuildsResponse(
- builds: <Build>[
- Build(
- id: '123',
- builderId: BuilderId(
- builder: 'builder_abc',
- bucket: 'try',
- project: 'flutter',
- ),
- tags: <String, List<String>>{
- 'buildset': <String>['pr/git/12345', 'sha/git/259bcf77bd04e64ef2181caccc43eda57780cd21'],
- 'cipd_version': <String>['refs/heads/main'],
- 'github_link': <String>['https://github/flutter/flutter/pull/1'],
- },
- input: Input(
- properties: <String, Object>{'bringup': 'true'},
- ),
- ),
- ],
- );
-
- @override
- Future<BatchResponse> batch(
- BatchRequest request, {
- String buildBucketUri = 'https://localhost/builds',
- }) async {
- if (batchResponse != null) {
- return batchResponse!();
- }
- final List<Response> responses = <Response>[];
- for (Request request in request.requests!) {
- if (request.cancelBuild != null) {
- responses.add(Response(cancelBuild: await cancelBuild(request.cancelBuild)));
- } else if (request.getBuild != null) {
- responses.add(Response(getBuild: await getBuild(request.getBuild)));
- } else if (request.scheduleBuild != null) {
- responses.add(Response(scheduleBuild: await scheduleBuild(request.scheduleBuild)));
- } else if (request.searchBuilds != null) {
- responses.add(Response(searchBuilds: await searchBuilds(request.searchBuilds)));
- }
- }
- return BatchResponse(responses: responses);
- }
-
- @override
- Future<Build> cancelBuild(
- CancelBuildRequest? request, {
- String buildBucketUri = 'https://localhost/builds',
- }) async =>
- (cancelBuildResponse != null)
- ? await cancelBuildResponse!
- : Build(
- id: request!.id,
- builderId: const BuilderId(
- bucket: 'try',
- project: 'flutter',
- builder: 'builder_abc',
- ),
- summaryMarkdown: request.summaryMarkdown,
- );
-
- @override
- Future<Build> getBuild(
- GetBuildRequest? request, {
- String buildBucketUri = 'https://localhost/builds',
- }) async =>
- (getBuildResponse != null)
- ? await getBuildResponse!
- : Build(
- id: request!.id!,
- builderId: request.builderId!,
- number: request.buildNumber,
- );
-
- @override
- Future<ListBuildersResponse> listBuilders(
- ListBuildersRequest? request, {
- String buildBucketUri = 'https://localhost/builders',
- }) async =>
- (listBuildersResponse != null)
- ? await listBuildersResponse!
- : const ListBuildersResponse(
- builders: <BuilderItem>[
- BuilderItem(
- id: BuilderId(
- bucket: 'prod',
- project: 'flutter',
- builder: 'Linux_android A',
- ),
- ),
- BuilderItem(
- id: BuilderId(
- bucket: 'prod',
- project: 'flutter',
- builder: 'Linux_android B',
- ),
- ),
- ],
- );
-}
diff --git a/app_dart/test/src/service/fake_gerrit_service.dart b/app_dart/test/src/service/fake_gerrit_service.dart
deleted file mode 100644
index e1f684e..0000000
--- a/app_dart/test/src/service/fake_gerrit_service.dart
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:cocoon_service/src/model/gerrit/commit.dart';
-import 'package:cocoon_service/src/request_handling/exceptions.dart';
-import 'package:cocoon_service/src/service/gerrit_service.dart';
-import 'package:collection/collection.dart';
-import 'package:github/github.dart';
-import 'package:http/testing.dart';
-
-import '../datastore/fake_config.dart';
-import '../utilities/entity_generators.dart';
-
-/// Fake [GerritService] for use in tests.
-class FakeGerritService extends GerritService {
- FakeGerritService({
- this.branchesValue = _defaultBranches,
- this.commitsValue,
- }) : super(
- config: FakeConfig(),
- httpClient:
- MockClient((_) => throw const InternalServerError('FakeGerritService tried to make an http request')),
- );
-
- List<String> branchesValue;
- static const List<String> _defaultBranches = <String>['main'];
-
- List<GerritCommit>? commitsValue;
- final List<GerritCommit> _defaultCommits = <GerritCommit>[
- generateGerritCommit(1),
- generateGerritCommit(2),
- generateGerritCommit(3),
- ];
-
- @override
- Future<List<String>> branches(String repo, String project, {String? filterRegex}) async => branchesValue;
-
- @override
- Future<Iterable<GerritCommit>> commits(RepositorySlug slug, String branch) async => commitsValue ?? _defaultCommits;
-
- @override
- Future<void> createBranch(RepositorySlug slug, String branchName, String revision) async => Future.value(null);
-
- @override
- Future<GerritCommit?> findMirroredCommit(RepositorySlug slug, String sha) async {
- final commits = await this.commits(slug, '');
- return commits.firstWhereOrNull((commit) => commit.commit == sha);
- }
-}
diff --git a/app_dart/test/src/service/fake_github_service.dart b/app_dart/test/src/service/fake_github_service.dart
deleted file mode 100644
index a0b9d6f..0000000
--- a/app_dart/test/src/service/fake_github_service.dart
+++ /dev/null
@@ -1,155 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:cocoon_service/src/service/github_service.dart';
-import 'package:github/github.dart';
-
-import '../utilities/mocks.dart';
-
-/// A fake GithubService implementation.
-class FakeGithubService implements GithubService {
- FakeGithubService({GitHub? client}) : github = client ?? MockGitHub();
- late List<RepositoryCommit> Function(String, int) listCommitsBranch;
- late List<PullRequest> Function(String?) listPullRequestsBranch;
-
- @override
- final GitHub github;
-
- @override
- Future<List<RepositoryCommit>> listBranchedCommits(
- RepositorySlug slug,
- String branch,
- int? lastCommitTimestampMills,
- ) async {
- return listCommitsBranch(branch, lastCommitTimestampMills ?? 0);
- }
-
- @override
- Future<List<PullRequest>> listPullRequests(RepositorySlug slug, String? branch) async {
- return listPullRequestsBranch(branch);
- }
-
- @override
- Future<List<IssueLabel>> addIssueLabels(
- RepositorySlug slug,
- int issueNumber,
- List<String> labels,
- ) async {
- return <IssueLabel>[];
- }
-
- @override
- Future<void> assignReviewer(RepositorySlug slug, {int? pullRequestNumber, String? reviewer}) async {}
-
- @override
- Future<Issue> createIssue(
- RepositorySlug slug, {
- String? title,
- String? body,
- List<String?>? labels,
- String? assignee,
- }) async {
- return Issue();
- }
-
- @override
- Future<void> assignIssue(
- RepositorySlug slug, {
- int? issueNumber,
- String? assignee,
- }) async {
- return;
- }
-
- @override
- Future<PullRequest> createPullRequest(
- RepositorySlug slug, {
- String? title,
- String? body,
- String? commitMessage,
- GitReference? baseRef,
- List<CreateGitTreeEntry>? entries,
- }) async {
- return PullRequest();
- }
-
- @override
- Future<String> getFileContent(RepositorySlug slug, String path, {String? ref}) async {
- if (path == 'bin/internal/release-candidate-branch.version') {
- if (ref == 'beta') {
- return 'flutter-3.2-candidate.5\n';
- } else if (ref == 'stable') {
- return 'flutter-2.13-candidate.0\n';
- }
- return Future<String>.value('');
- } else {
- return Future<String>.value('');
- }
- }
-
- @override
- Future<List<String>> listFiles(PullRequest pullRequest) async {
- return <String>['abc/def'];
- }
-
- @override
- Future<GitReference> getReference(RepositorySlug slug, String ref) async {
- return GitReference();
- }
-
- @override
- Future<List<Issue>> listIssues(
- RepositorySlug slug, {
- List<String>? labels,
- String state = 'open',
- }) async {
- return <Issue>[];
- }
-
- @override
- Future<Issue>? getIssue(
- RepositorySlug slug, {
- int? issueNumber,
- }) {
- return null;
- }
-
- @override
- Future<IssueComment?> createComment(
- RepositorySlug slug, {
- int? issueNumber,
- String? body,
- }) async {
- return null;
- }
-
- @override
- Future<List<IssueLabel>> replaceLabelsForIssue(
- RepositorySlug slug, {
- int? issueNumber,
- List<String>? labels,
- }) async {
- return <IssueLabel>[];
- }
-
- @override
- Future<RateLimit> getRateLimit() {
- throw UnimplementedError();
- }
-
- @override
- Future<PullRequest> getPullRequest(RepositorySlug slug, int number) async {
- return PullRequest();
- }
-
- @override
- Future<List<Issue>> searchIssuesAndPRs(
- RepositorySlug slug,
- String query, {
- String? sort,
- int pages = 2,
- }) async {
- return <Issue>[];
- }
-}
diff --git a/app_dart/test/src/service/fake_graphql_client.dart b/app_dart/test/src/service/fake_graphql_client.dart
deleted file mode 100644
index 9e1f9b2..0000000
--- a/app_dart/test/src/service/fake_graphql_client.dart
+++ /dev/null
@@ -1,127 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:gql/ast.dart';
-import 'package:graphql/client.dart';
-import 'package:test/test.dart';
-
-class FakeGraphQLClient implements GraphQLClient {
- late QueryResult Function(MutationOptions) mutateResultForOptions;
- late QueryResult Function(QueryOptions) queryResultForOptions;
-
- @override
- late QueryManager queryManager;
-
- @override
- Link get link => throw UnimplementedError();
-
- final List<QueryOptions> queries = <QueryOptions>[];
- final List<MutationOptions> mutations = <MutationOptions>[];
-
- @override
- Future<QueryResult<T>> mutate<T>(MutationOptions options) async {
- mutations.add(options);
- return mutateResultForOptions(options) as QueryResult<T>;
- }
-
- @override
- Future<QueryResult<T>> query<T>(QueryOptions options) async {
- queries.add(options);
- return queryResultForOptions(options) as QueryResult<T>;
- }
-
- void verifyQueries(List<QueryOptions> expected) {
- expect(queries.length, expected.length);
- for (int i = 0; i < queries.length; i++) {
- expect(
- queries[i].properties,
- equals(expected[i].properties),
- );
- }
- }
-
- void verifyMutations(List<MutationOptions> expected) {
- expect(mutations.length, expected.length);
- for (int i = 0; i < mutations.length; i++) {
- expect(
- mutations[i].properties,
- equals(expected[i].properties),
- );
- }
- }
-
- @override
- late DefaultPolicies defaultPolicies;
-
- @override
- Map<String, dynamic> readFragment(FragmentRequest fragmentRequest, {bool? optimistic = true}) {
- throw UnimplementedError();
- }
-
- @override
- Map<String, dynamic> readQuery(Request request, {bool? optimistic = true}) {
- throw UnimplementedError();
- }
-
- @override
- Future<List<QueryResult>> resetStore({bool refetchQueries = true}) {
- throw UnimplementedError();
- }
-
- @override
- void writeFragment(FragmentRequest fragmentRequest, {bool? broadcast = true, Map<String, dynamic>? data}) {}
-
- @override
- void writeQuery(Request request, {Map<String, dynamic>? data, bool? broadcast = true}) {}
-
- @override
- GraphQLCache get cache => throw UnimplementedError();
-
- @override
- GraphQLClient copyWith({
- Link? link,
- GraphQLCache? cache,
- DefaultPolicies? defaultPolicies,
- bool? alwaysRebroadcast,
- }) {
- throw UnimplementedError();
- }
-
- @override
- Future<QueryResult<T>> fetchMore<T>(
- FetchMoreOptions fetchMoreOptions, {
- required QueryOptions<T> originalOptions,
- required QueryResult<T> previousResult,
- }) {
- throw UnimplementedError();
- }
-
- @override
- Stream<QueryResult<T>> subscribe<T>(SubscriptionOptions<T> options) {
- throw UnimplementedError();
- }
-
- @override
- ObservableQuery<T> watchMutation<T>(WatchQueryOptions<T> options) {
- throw UnimplementedError();
- }
-
- @override
- ObservableQuery<T> watchQuery<T>(WatchQueryOptions<T> options) {
- throw UnimplementedError();
- }
-}
-
-QueryResult createFakeQueryResult({
- Map<String, dynamic>? data,
- OperationException? exception,
-}) =>
- QueryResult(
- data: data,
- exception: exception,
- options: QueryOptions(
- document: const DocumentNode(),
- ),
- source: QueryResultSource.network,
- );
diff --git a/app_dart/test/src/service/fake_luci_build_service.dart b/app_dart/test/src/service/fake_luci_build_service.dart
deleted file mode 100644
index 6650085..0000000
--- a/app_dart/test/src/service/fake_luci_build_service.dart
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2021 The Flutter 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 'package:cocoon_service/src/foundation/github_checks_util.dart';
-import 'package:cocoon_service/src/request_handling/pubsub.dart';
-import 'package:cocoon_service/src/service/buildbucket.dart';
-import 'package:cocoon_service/src/service/cache_service.dart';
-import 'package:cocoon_service/src/service/gerrit_service.dart';
-import 'package:cocoon_service/src/service/luci_build_service.dart';
-
-import '../request_handling/fake_pubsub.dart';
-import '../utilities/mocks.dart';
-import 'fake_buildbucket.dart';
-import 'fake_gerrit_service.dart';
-
-/// Fake [LuciBuildService] for use in tests.
-class FakeLuciBuildService extends LuciBuildService {
- FakeLuciBuildService({
- required super.config,
- BuildBucketClient? buildbucket,
- GithubChecksUtil? githubChecksUtil,
- GerritService? gerritService,
- PubSub? pubsub,
- }) : super(
- cache: CacheService(inMemory: true),
- buildBucketClient: buildbucket ?? FakeBuildBucketClient(),
- githubChecksUtil: githubChecksUtil ?? MockGithubChecksUtil(),
- gerritService: gerritService ?? FakeGerritService(),
- pubsub: pubsub ?? FakePubSub(),
- );
-}
diff --git a/app_dart/test/src/service/fake_scheduler.dart b/app_dart/test/src/service/fake_scheduler.dart
deleted file mode 100644
index c07e772..0000000
--- a/app_dart/test/src/service/fake_scheduler.dart
+++ /dev/null
@@ -1,294 +0,0 @@
-// Copyright 2021 The Flutter 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 'package:cocoon_service/src/foundation/github_checks_util.dart';
-import 'package:cocoon_service/src/model/appengine/commit.dart';
-import 'package:cocoon_service/src/model/ci_yaml/ci_yaml.dart';
-import 'package:cocoon_service/src/model/proto/protos.dart' as pb;
-import 'package:cocoon_service/src/service/buildbucket.dart';
-import 'package:cocoon_service/src/service/cache_service.dart';
-import 'package:cocoon_service/src/service/config.dart';
-import 'package:cocoon_service/src/service/github_checks_service.dart';
-import 'package:cocoon_service/src/service/luci_build_service.dart';
-import 'package:cocoon_service/src/service/scheduler.dart';
-import 'package:github/github.dart';
-import 'package:retry/retry.dart';
-
-import '../utilities/entity_generators.dart';
-import 'fake_luci_build_service.dart';
-
-/// Fake for [Scheduler] to use for tests that rely on it.
-class FakeScheduler extends Scheduler {
- FakeScheduler({
- this.ciYaml,
- LuciBuildService? luciBuildService,
- BuildBucketClient? buildbucket,
- required super.config,
- GithubChecksUtil? githubChecksUtil,
- }) : super(
- cache: CacheService(inMemory: true),
- githubChecksService: GithubChecksService(
- config,
- githubChecksUtil: githubChecksUtil,
- ),
- luciBuildService: luciBuildService ??
- FakeLuciBuildService(
- config: config,
- buildbucket: buildbucket,
- githubChecksUtil: githubChecksUtil,
- ),
- );
-
- final CiYaml _defaultConfig = emptyConfig;
-
- /// [CiYaml] value to be injected on [getCiYaml].
- CiYaml? ciYaml;
-
- @override
- Future<CiYaml> getCiYaml(
- Commit commit, {
- CiYaml? totCiYaml,
- RetryOptions? retryOptions,
- bool validate = false,
- }) async =>
- ciYaml ?? _defaultConfig;
-
- @override
- Future<Commit> generateTotCommit({required String branch, required RepositorySlug slug}) async {
- return generateCommit(1);
- }
-
- int cancelPreSubmitTargetsCallCnt = 0;
-
- int get cancelPreSubmitTargetsCallCount {
- return cancelPreSubmitTargetsCallCnt;
- }
-
- void resetCancelPreSubmitTargetsCallCount() {
- cancelPreSubmitTargetsCallCnt = 0;
- }
-
- @override
- Future<void> cancelPreSubmitTargets({
- required PullRequest pullRequest,
- String reason = 'Newer commit available',
- }) async {
- await super.cancelPreSubmitTargets(pullRequest: pullRequest);
- cancelPreSubmitTargetsCallCnt++;
- }
-
- int addPullRequestCallCnt = 0;
-
- int get addPullRequestCallCount {
- return addPullRequestCallCnt;
- }
-
- void resetAddPullRequestCallCount() {
- addPullRequestCallCnt = 0;
- }
-
- @override
- Future<void> addPullRequest(
- PullRequest pr,
- ) async {
- await super.addPullRequest(pr);
- addPullRequestCallCnt++;
- }
-}
-
-final CiYaml emptyConfig = CiYaml(
- slug: Config.flutterSlug,
- branch: Config.defaultBranch(Config.flutterSlug),
- config: pb.SchedulerConfig(
- enabledBranches: <String>[
- Config.defaultBranch(Config.flutterSlug),
- ],
- targets: <pb.Target>[
- pb.Target(
- name: 'Linux A',
- scheduler: pb.SchedulerSystem.luci,
- ),
- ],
- ),
-);
-
-CiYaml exampleConfig = CiYaml(
- slug: Config.flutterSlug,
- branch: Config.defaultBranch(Config.flutterSlug),
- config: pb.SchedulerConfig(
- enabledBranches: <String>[
- Config.defaultBranch(Config.flutterSlug),
- ],
- targets: <pb.Target>[
- pb.Target(
- name: 'Linux A',
- scheduler: pb.SchedulerSystem.luci,
- ),
- pb.Target(
- name: 'Mac A',
- scheduler: pb.SchedulerSystem.luci,
- ),
- pb.Target(
- name: 'Windows A',
- scheduler: pb.SchedulerSystem.luci,
- ),
- pb.Target(
- name: 'Google Internal Roll',
- presubmit: false,
- postsubmit: true,
- scheduler: pb.SchedulerSystem.google_internal,
- ),
- ],
- ),
-);
-
-CiYaml exampleBackfillConfig = CiYaml(
- slug: Config.flutterSlug,
- branch: Config.defaultBranch(Config.flutterSlug),
- config: pb.SchedulerConfig(
- enabledBranches: <String>[
- Config.defaultBranch(Config.flutterSlug),
- ],
- targets: <pb.Target>[
- pb.Target(
- name: 'Linux A',
- scheduler: pb.SchedulerSystem.luci,
- postsubmit: true,
- properties: {'backfill': 'true'},
- ),
- pb.Target(
- name: 'Mac A',
- scheduler: pb.SchedulerSystem.luci,
- postsubmit: true,
- ),
- pb.Target(
- name: 'Windows A',
- scheduler: pb.SchedulerSystem.luci,
- postsubmit: true,
- properties: {'backfill': 'false'},
- ),
- ],
- ),
-);
-
-CiYaml examplePresubmitRescheduleConfig = CiYaml(
- slug: Config.flutterSlug,
- branch: Config.defaultBranch(Config.flutterSlug),
- config: pb.SchedulerConfig(
- enabledBranches: <String>[
- Config.defaultBranch(Config.flutterSlug),
- ],
- targets: <pb.Target>[
- pb.Target(
- name: 'Linux A',
- ),
- pb.Target(
- name: 'Linux B',
- postsubmit: true,
- properties: {'presubmit_retry': '1'},
- ),
- ],
- ),
-);
-
-final CiYaml batchPolicyConfig = CiYaml(
- slug: Config.flutterSlug,
- branch: Config.defaultBranch(Config.flutterSlug),
- config: pb.SchedulerConfig(
- enabledBranches: <String>[
- Config.defaultBranch(Config.flutterSlug),
- ],
- targets: <pb.Target>[
- pb.Target(
- name: 'Linux_android A',
- ),
- pb.Target(
- name: 'Linux_android B',
- ),
- pb.Target(
- name: 'Linux_android C',
- ),
- ],
- ),
-);
-
-final CiYaml unsupportedPostsubmitCheckrunConfig = CiYaml(
- slug: Config.flutterSlug,
- branch: Config.defaultBranch(Config.flutterSlug),
- config: pb.SchedulerConfig(
- enabledBranches: <String>[
- Config.defaultBranch(Config.flutterSlug),
- ],
- targets: <pb.Target>[
- pb.Target(
- name: 'Linux flutter',
- ),
- ],
- ),
-);
-
-final CiYaml nonBringupPackagesConfig = CiYaml(
- slug: Config.packagesSlug,
- branch: Config.defaultBranch(Config.packagesSlug),
- config: pb.SchedulerConfig(
- enabledBranches: <String>[
- Config.defaultBranch(Config.packagesSlug),
- ],
- targets: <pb.Target>[
- pb.Target(
- name: 'Linux nonbringup',
- ),
- ],
- ),
-);
-
-final CiYaml bringupPackagesConfig = CiYaml(
- slug: Config.packagesSlug,
- branch: Config.defaultBranch(Config.packagesSlug),
- config: pb.SchedulerConfig(
- enabledBranches: <String>[
- Config.defaultBranch(Config.packagesSlug),
- ],
- targets: <pb.Target>[
- pb.Target(
- name: 'Linux bringup',
- bringup: true,
- ),
- ],
- ),
-);
-
-final CiYaml totCiYaml = CiYaml(
- slug: Config.flutterSlug,
- branch: Config.defaultBranch(Config.flutterSlug),
- config: pb.SchedulerConfig(
- enabledBranches: <String>[
- Config.defaultBranch(Config.flutterSlug),
- ],
- targets: <pb.Target>[
- pb.Target(
- name: 'Linux_android B',
- ),
- pb.Target(
- name: 'Linux_android C',
- ),
- ],
- ),
-);
-
-final CiYaml notInToTConfig = CiYaml(
- slug: Config.flutterSlug,
- branch: Config.defaultBranch(Config.flutterSlug),
- config: pb.SchedulerConfig(
- enabledBranches: <String>[
- Config.defaultBranch(Config.flutterSlug),
- ],
- targets: <pb.Target>[
- pb.Target(
- name: 'Linux_android A',
- ),
- ],
- ),
- totConfig: totCiYaml,
-);
diff --git a/app_dart/test/src/utilities/entity_generators.dart b/app_dart/test/src/utilities/entity_generators.dart
deleted file mode 100644
index a3ac9d9..0000000
--- a/app_dart/test/src/utilities/entity_generators.dart
+++ /dev/null
@@ -1,296 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:cocoon_service/ci_yaml.dart';
-import 'package:cocoon_service/src/model/appengine/commit.dart';
-import 'package:cocoon_service/src/model/appengine/task.dart';
-import 'package:cocoon_service/src/model/ci_yaml/target.dart';
-import 'package:cocoon_service/src/model/gerrit/commit.dart';
-import 'package:cocoon_service/src/model/luci/buildbucket.dart';
-import 'package:cocoon_service/src/model/luci/push_message.dart' as push_message;
-import 'package:cocoon_service/src/model/proto/protos.dart' as pb;
-import 'package:gcloud/db.dart';
-import 'package:github/github.dart' as github;
-
-import '../service/fake_scheduler.dart';
-
-Key<T> generateKey<T>(Type type, T id) => Key<T>.emptyKey(Partition(null)).append<T>(type, id: id);
-
-Commit generateCommit(
- int i, {
- String? sha,
- String branch = 'master',
- String? owner = 'flutter',
- String repo = 'flutter',
- int? timestamp,
-}) =>
- Commit(
- sha: sha ?? '$i',
- timestamp: timestamp ?? i,
- repository: '$owner/$repo',
- branch: branch,
- key: generateKey<String>(
- Commit,
- '$owner/$repo/$branch/${sha ?? '$i'}',
- ),
- );
-
-github.Branch generateBranch(
- int i, {
- String? name,
- String? sha,
-}) =>
- github.Branch(
- name ?? '$i',
- github.CommitData(
- sha,
- github.GitCommit(),
- null,
- null,
- null,
- null,
- null,
- null,
- ),
- );
-
-github.Tag generateTag(
- int i, {
- String? name,
- String? sha,
-}) =>
- github.Tag(
- name ?? '$i',
- github.CommitInfo(
- sha,
- null,
- ),
- 'blah_zip',
- 'blah_tar',
- );
-
-Task generateTask(
- int i, {
- String? name,
- String status = Task.statusNew,
- int attempts = 1,
- bool isFlaky = false,
- String stage = 'test-stage',
- Commit? parent,
- int? buildNumber,
- DateTime? created,
-}) =>
- Task(
- name: name ?? 'task$i',
- status: status,
- commitKey: parent?.key ?? generateCommit(i).key,
- key: (parent ?? generateCommit(i)).key.append(Task, id: i),
- attempts: attempts,
- isFlaky: isFlaky,
- buildNumber: buildNumber,
- buildNumberList: buildNumber != null ? '$buildNumber' : null,
- createTimestamp: created?.millisecondsSinceEpoch ?? 0,
- stageName: stage,
- );
-
-Target generateTarget(
- int i, {
- pb.SchedulerConfig? schedulerConfig,
- String platform = 'Linux',
- Map<String, String>? platformProperties,
- Map<String, String>? platformDimensions,
- Map<String, String>? properties,
- Map<String, String>? dimensions,
- List<String>? runIf,
- List<String>? runIfNot,
- bool? bringup,
- github.RepositorySlug? slug,
- pb.SchedulerSystem? schedulerSystem,
-}) {
- final pb.SchedulerConfig config = schedulerConfig ?? exampleConfig.config;
- if (platformProperties != null && platformDimensions != null) {
- config.platformProperties[platform.toLowerCase()] =
- pb.SchedulerConfig_PlatformProperties(properties: platformProperties, dimensions: platformDimensions);
- } else if (platformDimensions != null) {
- config.platformProperties[platform.toLowerCase()] =
- pb.SchedulerConfig_PlatformProperties(dimensions: platformDimensions);
- } else if (platformProperties != null) {
- config.platformProperties[platform.toLowerCase()] =
- pb.SchedulerConfig_PlatformProperties(properties: platformProperties);
- }
- return Target(
- schedulerConfig: config,
- slug: slug ?? github.RepositorySlug('flutter', 'flutter'),
- value: pb.Target(
- name: '$platform $i',
- properties: properties,
- dimensions: dimensions,
- runIf: runIf ?? <String>[],
- runIfNot: runIfNot ?? <String>[],
- bringup: bringup ?? false,
- scheduler: schedulerSystem ?? pb.SchedulerSystem.cocoon,
- ),
- );
-}
-
-Build generateBuild(
- int i, {
- String bucket = 'prod',
- String name = 'Linux test_builder',
- Status status = Status.success,
- Map<String?, List<String?>>? tags,
- Input? input,
- int buildNumber = 1,
-}) =>
- Build(
- id: i.toString(),
- builderId: BuilderId(
- project: 'flutter',
- bucket: bucket,
- builder: name,
- ),
- status: status,
- tags: tags,
- number: buildNumber,
- input: input,
- );
-
-push_message.Build generatePushMessageBuild(
- int i, {
- String bucket = 'prod',
- String name = 'Linux test_builder',
- push_message.Status? status = push_message.Status.completed,
- push_message.Result result = push_message.Result.success,
- List<String>? tags,
- int buildNumber = 1,
- DateTime? completedTimestamp,
- DateTime? createdTimestamp,
- DateTime? startedTimestamp,
- push_message.FailureReason? failureReason,
-}) {
- tags ??= <String>[];
- tags.add('build_address:luci.flutter.prod/$name/$buildNumber');
-
- return push_message.Build(
- bucket: bucket,
- id: i.toString(),
- project: 'flutter',
- status: status,
- result: result,
- createdTimestamp: createdTimestamp,
- completedTimestamp: completedTimestamp,
- startedTimestamp: startedTimestamp,
- tags: tags,
- failureReason: failureReason,
- );
-}
-
-github.CheckRun generateCheckRun(
- int i, {
- String name = 'name',
- int checkSuite = 2,
- DateTime? startedAt,
-}) {
- startedAt ??= DateTime.utc(2020, 05, 12);
- return github.CheckRun.fromJson(<String, dynamic>{
- 'id': i,
- 'name': name,
- 'started_at': startedAt.toIso8601String(),
- 'check_suite': <String, dynamic>{'id': checkSuite},
- });
-}
-
-github.CheckSuite generateCheckSuite(
- int i, {
- String headBranch = 'main',
- String headSha = 'abc',
- github.CheckRunConclusion conclusion = github.CheckRunConclusion.success,
- List<github.PullRequest> pullRequests = const <github.PullRequest>[],
-}) {
- return github.CheckSuite(
- id: i,
- headBranch: headBranch,
- headSha: headSha,
- conclusion: conclusion,
- pullRequests: pullRequests,
- );
-}
-
-github.PullRequest generatePullRequest({
- int id = 789,
- String branch = 'master',
- String repo = 'flutter',
- String authorLogin = 'dash',
- String authorAvatar = 'dashatar',
- String title = 'example message',
- int number = 123,
- DateTime? mergedAt,
- String sha = 'abc',
- bool merged = true,
- List<github.IssueLabel> labels = const [],
-}) {
- mergedAt ??= DateTime.fromMillisecondsSinceEpoch(1);
- return github.PullRequest(
- id: id,
- title: title,
- number: number,
- mergedAt: mergedAt,
- base: github.PullRequestHead(
- ref: branch,
- repo: github.Repository(
- fullName: 'flutter/$repo',
- name: repo,
- owner: github.UserInformation('flutter', 1, '', ''),
- ),
- ),
- head: github.PullRequestHead(
- ref: branch,
- sha: sha,
- ),
- user: github.User(
- login: authorLogin,
- avatarUrl: authorAvatar,
- ),
- mergeCommitSha: sha,
- merged: merged,
- labels: labels,
- );
-}
-
-GerritCommit generateGerritCommit(int i) => GerritCommit(
- commit: 'sha$i',
- tree: 'main',
- author: GerritUser(
- email: 'dash@flutter.dev',
- time: DateTime.fromMillisecondsSinceEpoch(i),
- ),
- );
-
-github.RepositoryCommit generateGitCommit(int i) => github.RepositoryCommit(
- commit: github.GitCommit(
- committer: github.GitCommitUser(
- 'dash',
- 'dash@flutter.dev',
- DateTime.fromMillisecondsSinceEpoch(i),
- ),
- ),
- );
-
-github.Issue generateIssue(
- int i, {
- String authorLogin = 'dash',
- String authorAvatar = 'dashatar',
- String title = 'example message',
- int number = 123,
-}) {
- return github.Issue(
- id: i,
- title: title,
- number: number,
- user: github.User(
- login: authorLogin,
- avatarUrl: authorAvatar,
- ),
- );
-}
diff --git a/app_dart/test/src/utilities/matchers.dart b/app_dart/test/src/utilities/matchers.dart
deleted file mode 100644
index ce8605a..0000000
--- a/app_dart/test/src/utilities/matchers.dart
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2021 The Flutter 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 'package:test/test.dart';
-
-Matcher throwsExceptionWith<T>(String messageSubString) {
- return throwsA(
- isA<T>().having(
- (T e) => e.toString(),
- 'description',
- contains(messageSubString),
- ),
- );
-}
diff --git a/app_dart/test/src/utilities/mocks.dart b/app_dart/test/src/utilities/mocks.dart
deleted file mode 100644
index 8451cd1..0000000
--- a/app_dart/test/src/utilities/mocks.dart
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2020 The Flutter 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 'dart:typed_data';
-
-import 'package:cocoon_service/src/foundation/github_checks_util.dart';
-import 'package:cocoon_service/src/request_handling/exceptions.dart';
-import 'package:cocoon_service/src/service/access_client_provider.dart';
-import 'package:cocoon_service/src/service/access_token_provider.dart';
-import 'package:cocoon_service/src/service/bigquery.dart';
-import 'package:cocoon_service/src/service/branch_service.dart';
-import 'package:cocoon_service/src/service/buildbucket.dart';
-import 'package:cocoon_service/src/service/commit_service.dart';
-import 'package:cocoon_service/src/service/config.dart';
-import 'package:cocoon_service/src/service/datastore.dart';
-import 'package:cocoon_service/src/service/github_checks_service.dart';
-import 'package:cocoon_service/src/service/github_service.dart';
-import 'package:cocoon_service/src/service/luci_build_service.dart';
-import 'package:github/github.dart';
-import 'package:googleapis/bigquery/v2.dart';
-import 'package:googleapis_auth/googleapis_auth.dart';
-import 'package:graphql/client.dart';
-import 'package:http/http.dart' as http;
-import 'package:http/testing.dart';
-import 'package:mockito/annotations.dart';
-import 'package:neat_cache/neat_cache.dart';
-import 'package:process/process.dart';
-
-import '../../service/cache_service_test.dart';
-import '../service/fake_auth_client.dart';
-
-export 'mocks.mocks.dart';
-
-/// Fallback generated function for returning default values from the generic
-/// function [GitHub.postJSON].
-Future<T> postJsonShim<S, T>(
- dynamic path, {
- int? statusCode,
- void Function(http.Response)? fail,
- Map<String, String>? headers,
- Map<String, dynamic>? params,
- T Function(S)? convert,
- dynamic body,
- String? preview,
-}) {
- if (T == PullRequest) {
- return Future<T>.value(PullRequest() as T);
- }
- throw Exception('MockGitHub.postJSON does not return $T.\n'
- 'Either add it to postJsonShim or use a manual mock.');
-}
-
-Future<AutoRefreshingAuthClient> authClientProviderShim({
- http.Client? baseClient,
- required List<String> scopes,
-}) async =>
- FakeAuthClient(baseClient ?? MockClient((_) => throw const InternalServerError('Test did not set up HttpClient')));
-
-@GenerateMocks(
- <Type>[
- AccessClientProvider,
- AccessTokenService,
- BigqueryService,
- BranchService,
- BuildBucketClient,
- CommitService,
- Config,
- DatastoreService,
- FakeEntry,
- IssuesService,
- GithubChecksService,
- GithubChecksUtil,
- GithubService,
- GitService,
- GraphQLClient,
- HttpClient,
- HttpClientRequest,
- HttpClientResponse,
- JobsResource,
- LuciBuildService,
- ProcessManager,
- PullRequestsService,
- RepositoriesService,
- SearchService,
- TabledataResource,
- UsersService,
- ],
- customMocks: [
- MockSpec<Cache<Uint8List>>(),
- MockSpec<GitHub>(
- fallbackGenerators: <Symbol, Function>{
- #postJSON: postJsonShim,
- },
- ),
- ],
-)
-void main() {}
-
-class ThrowingGitHub implements GitHub {
- @override
- dynamic noSuchMethod(Invocation invocation) => throw AssertionError();
-}
diff --git a/app_dart/test/src/utilities/mocks.mocks.dart b/app_dart/test/src/utilities/mocks.mocks.dart
deleted file mode 100644
index a9f101f..0000000
--- a/app_dart/test/src/utilities/mocks.mocks.dart
+++ /dev/null
@@ -1,8811 +0,0 @@
-// Mocks generated by Mockito 5.4.2 from annotations
-// in cocoon_service/test/src/utilities/mocks.dart.
-// Do not manually edit this file.
-
-// ignore_for_file: no_leading_underscores_for_library_prefixes
-import 'dart:async' as _i19;
-import 'dart:convert' as _i22;
-import 'dart:io' as _i21;
-import 'dart:typed_data' as _i35;
-
-import 'package:appengine/appengine.dart' as _i10;
-import 'package:cocoon_service/cocoon_service.dart' as _i23;
-import 'package:cocoon_service/src/foundation/github_checks_util.dart' as _i20;
-import 'package:cocoon_service/src/model/appengine/branch.dart' as _i30;
-import 'package:cocoon_service/src/model/appengine/commit.dart' as _i31;
-import 'package:cocoon_service/src/model/appengine/github_build_status_update.dart' as _i17;
-import 'package:cocoon_service/src/model/appengine/github_gold_status_update.dart' as _i18;
-import 'package:cocoon_service/src/model/appengine/key_helper.dart' as _i12;
-import 'package:cocoon_service/src/model/appengine/stage.dart' as _i33;
-import 'package:cocoon_service/src/model/appengine/task.dart' as _i32;
-import 'package:cocoon_service/src/model/ci_yaml/target.dart' as _i37;
-import 'package:cocoon_service/src/model/github/checks.dart' as _i38;
-import 'package:cocoon_service/src/model/luci/buildbucket.dart' as _i8;
-import 'package:cocoon_service/src/model/luci/push_message.dart' as _i36;
-import 'package:cocoon_service/src/service/access_client_provider.dart' as _i5;
-import 'package:cocoon_service/src/service/access_token_provider.dart' as _i25;
-import 'package:cocoon_service/src/service/bigquery.dart' as _i15;
-import 'package:cocoon_service/src/service/commit_service.dart' as _i28;
-import 'package:cocoon_service/src/service/config.dart' as _i3;
-import 'package:cocoon_service/src/service/datastore.dart' as _i9;
-import 'package:cocoon_service/src/service/gerrit_service.dart' as _i7;
-import 'package:cocoon_service/src/service/github_service.dart' as _i16;
-import 'package:gcloud/db.dart' as _i11;
-import 'package:github/github.dart' as _i13;
-import 'package:github/hooks.dart' as _i29;
-import 'package:googleapis/bigquery/v2.dart' as _i6;
-import 'package:googleapis_auth/auth_io.dart' as _i4;
-import 'package:graphql/client.dart' as _i14;
-import 'package:http/http.dart' as _i2;
-import 'package:mockito/mockito.dart' as _i1;
-import 'package:mockito/src/dummies.dart' as _i27;
-import 'package:neat_cache/neat_cache.dart' as _i24;
-import 'package:process/src/interface/process_manager.dart' as _i39;
-import 'package:retry/retry.dart' as _i26;
-
-import '../../service/cache_service_test.dart' as _i34;
-import 'mocks.dart' as _i40;
-
-// ignore_for_file: type=lint
-// ignore_for_file: avoid_redundant_argument_values
-// ignore_for_file: avoid_setters_without_getters
-// ignore_for_file: comment_references
-// ignore_for_file: implementation_imports
-// ignore_for_file: invalid_use_of_visible_for_testing_member
-// ignore_for_file: prefer_const_constructors
-// ignore_for_file: unnecessary_parenthesis
-// ignore_for_file: camel_case_types
-// ignore_for_file: subtype_of_sealed_class
-
-class _FakeClient_0 extends _i1.SmartFake implements _i2.Client {
- _FakeClient_0(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeConfig_1 extends _i1.SmartFake implements _i3.Config {
- _FakeConfig_1(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeAccessToken_2 extends _i1.SmartFake implements _i4.AccessToken {
- _FakeAccessToken_2(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeAccessClientProvider_3 extends _i1.SmartFake implements _i5.AccessClientProvider {
- _FakeAccessClientProvider_3(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeTabledataResource_4 extends _i1.SmartFake implements _i6.TabledataResource {
- _FakeTabledataResource_4(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeJobsResource_5 extends _i1.SmartFake implements _i6.JobsResource {
- _FakeJobsResource_5(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeGerritService_6 extends _i1.SmartFake implements _i7.GerritService {
- _FakeGerritService_6(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeBuild_7 extends _i1.SmartFake implements _i8.Build {
- _FakeBuild_7(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeSearchBuildsResponse_8 extends _i1.SmartFake implements _i8.SearchBuildsResponse {
- _FakeSearchBuildsResponse_8(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeBatchResponse_9 extends _i1.SmartFake implements _i8.BatchResponse {
- _FakeBatchResponse_9(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeListBuildersResponse_10 extends _i1.SmartFake implements _i8.ListBuildersResponse {
- _FakeListBuildersResponse_10(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeDatastoreService_11 extends _i1.SmartFake implements _i9.DatastoreService {
- _FakeDatastoreService_11(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeLogging_12 extends _i1.SmartFake implements _i10.Logging {
- _FakeLogging_12(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeDatastoreDB_13 extends _i1.SmartFake implements _i11.DatastoreDB {
- _FakeDatastoreDB_13(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeKeyHelper_14 extends _i1.SmartFake implements _i12.KeyHelper {
- _FakeKeyHelper_14(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeDuration_15 extends _i1.SmartFake implements Duration {
- _FakeDuration_15(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeGitHub_16 extends _i1.SmartFake implements _i13.GitHub {
- _FakeGitHub_16(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeGraphQLClient_17 extends _i1.SmartFake implements _i14.GraphQLClient {
- _FakeGraphQLClient_17(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeBigqueryService_18 extends _i1.SmartFake implements _i15.BigqueryService {
- _FakeBigqueryService_18(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeGithubService_19 extends _i1.SmartFake implements _i16.GithubService {
- _FakeGithubService_19(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeGithubBuildStatusUpdate_20 extends _i1.SmartFake implements _i17.GithubBuildStatusUpdate {
- _FakeGithubBuildStatusUpdate_20(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeGithubGoldStatusUpdate_21 extends _i1.SmartFake implements _i18.GithubGoldStatusUpdate {
- _FakeGithubGoldStatusUpdate_21(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeFuture_22<T1> extends _i1.SmartFake implements _i19.Future<T1> {
- _FakeFuture_22(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeIssue_23 extends _i1.SmartFake implements _i13.Issue {
- _FakeIssue_23(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeIssueComment_24 extends _i1.SmartFake implements _i13.IssueComment {
- _FakeIssueComment_24(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeIssueLabel_25 extends _i1.SmartFake implements _i13.IssueLabel {
- _FakeIssueLabel_25(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeMilestone_26 extends _i1.SmartFake implements _i13.Milestone {
- _FakeMilestone_26(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeGithubChecksUtil_27 extends _i1.SmartFake implements _i20.GithubChecksUtil {
- _FakeGithubChecksUtil_27(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeCheckRunConclusion_28 extends _i1.SmartFake implements _i13.CheckRunConclusion {
- _FakeCheckRunConclusion_28(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeCheckRunStatus_29 extends _i1.SmartFake implements _i13.CheckRunStatus {
- _FakeCheckRunStatus_29(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeCheckSuite_30 extends _i1.SmartFake implements _i13.CheckSuite {
- _FakeCheckSuite_30(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeCheckRun_31 extends _i1.SmartFake implements _i13.CheckRun {
- _FakeCheckRun_31(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakePullRequest_32 extends _i1.SmartFake implements _i13.PullRequest {
- _FakePullRequest_32(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeGitReference_33 extends _i1.SmartFake implements _i13.GitReference {
- _FakeGitReference_33(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeRateLimit_34 extends _i1.SmartFake implements _i13.RateLimit {
- _FakeRateLimit_34(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeGitBlob_35 extends _i1.SmartFake implements _i13.GitBlob {
- _FakeGitBlob_35(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeGitCommit_36 extends _i1.SmartFake implements _i13.GitCommit {
- _FakeGitCommit_36(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeGitTag_37 extends _i1.SmartFake implements _i13.GitTag {
- _FakeGitTag_37(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeGitTree_38 extends _i1.SmartFake implements _i13.GitTree {
- _FakeGitTree_38(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeDefaultPolicies_39 extends _i1.SmartFake implements _i14.DefaultPolicies {
- _FakeDefaultPolicies_39(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeLink_40 extends _i1.SmartFake implements _i14.Link {
- _FakeLink_40(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeGraphQLCache_41 extends _i1.SmartFake implements _i14.GraphQLCache {
- _FakeGraphQLCache_41(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeQueryManager_42 extends _i1.SmartFake implements _i14.QueryManager {
- _FakeQueryManager_42(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeObservableQuery_43<TParsed1> extends _i1.SmartFake implements _i14.ObservableQuery<TParsed1> {
- _FakeObservableQuery_43(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeQueryResult_44<TParsed1 extends Object?> extends _i1.SmartFake implements _i14.QueryResult<TParsed1> {
- _FakeQueryResult_44(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeHttpClientRequest_45 extends _i1.SmartFake implements _i21.HttpClientRequest {
- _FakeHttpClientRequest_45(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeUri_46 extends _i1.SmartFake implements Uri {
- _FakeUri_46(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeHttpHeaders_47 extends _i1.SmartFake implements _i21.HttpHeaders {
- _FakeHttpHeaders_47(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeHttpClientResponse_48 extends _i1.SmartFake implements _i21.HttpClientResponse {
- _FakeHttpClientResponse_48(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeEncoding_49 extends _i1.SmartFake implements _i22.Encoding {
- _FakeEncoding_49(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeSocket_50 extends _i1.SmartFake implements _i21.Socket {
- _FakeSocket_50(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeStreamSubscription_51<T> extends _i1.SmartFake implements _i19.StreamSubscription<T> {
- _FakeStreamSubscription_51(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeJobCancelResponse_52 extends _i1.SmartFake implements _i6.JobCancelResponse {
- _FakeJobCancelResponse_52(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeJob_53 extends _i1.SmartFake implements _i6.Job {
- _FakeJob_53(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeGetQueryResultsResponse_54 extends _i1.SmartFake implements _i6.GetQueryResultsResponse {
- _FakeGetQueryResultsResponse_54(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeJobList_55 extends _i1.SmartFake implements _i6.JobList {
- _FakeJobList_55(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeQueryResponse_56 extends _i1.SmartFake implements _i6.QueryResponse {
- _FakeQueryResponse_56(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeBuildBucketClient_57 extends _i1.SmartFake implements _i23.BuildBucketClient {
- _FakeBuildBucketClient_57(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeCacheService_58 extends _i1.SmartFake implements _i23.CacheService {
- _FakeCacheService_58(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakePubSub_59 extends _i1.SmartFake implements _i23.PubSub {
- _FakePubSub_59(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeProcess_60 extends _i1.SmartFake implements _i21.Process {
- _FakeProcess_60(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakePullRequestMerge_61 extends _i1.SmartFake implements _i13.PullRequestMerge {
- _FakePullRequestMerge_61(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakePullRequestComment_62 extends _i1.SmartFake implements _i13.PullRequestComment {
- _FakePullRequestComment_62(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakePullRequestReview_63 extends _i1.SmartFake implements _i13.PullRequestReview {
- _FakePullRequestReview_63(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeRepository_64 extends _i1.SmartFake implements _i13.Repository {
- _FakeRepository_64(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeLicenseDetails_65 extends _i1.SmartFake implements _i13.LicenseDetails {
- _FakeLicenseDetails_65(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeLanguageBreakdown_66 extends _i1.SmartFake implements _i13.LanguageBreakdown {
- _FakeLanguageBreakdown_66(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeBranch_67 extends _i1.SmartFake implements _i13.Branch {
- _FakeBranch_67(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeCommitComment_68 extends _i1.SmartFake implements _i13.CommitComment {
- _FakeCommitComment_68(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeRepositoryCommit_69 extends _i1.SmartFake implements _i13.RepositoryCommit {
- _FakeRepositoryCommit_69(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeGitHubComparison_70 extends _i1.SmartFake implements _i13.GitHubComparison {
- _FakeGitHubComparison_70(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeGitHubFile_71 extends _i1.SmartFake implements _i13.GitHubFile {
- _FakeGitHubFile_71(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeRepositoryContents_72 extends _i1.SmartFake implements _i13.RepositoryContents {
- _FakeRepositoryContents_72(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeContentCreation_73 extends _i1.SmartFake implements _i13.ContentCreation {
- _FakeContentCreation_73(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeHook_74 extends _i1.SmartFake implements _i13.Hook {
- _FakeHook_74(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakePublicKey_75 extends _i1.SmartFake implements _i13.PublicKey {
- _FakePublicKey_75(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeRepositoryPages_76 extends _i1.SmartFake implements _i13.RepositoryPages {
- _FakeRepositoryPages_76(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakePageBuild_77 extends _i1.SmartFake implements _i13.PageBuild {
- _FakePageBuild_77(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeRelease_78 extends _i1.SmartFake implements _i13.Release {
- _FakeRelease_78(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeReleaseAsset_79 extends _i1.SmartFake implements _i13.ReleaseAsset {
- _FakeReleaseAsset_79(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeContributorParticipation_80 extends _i1.SmartFake implements _i13.ContributorParticipation {
- _FakeContributorParticipation_80(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeRepositoryStatus_81 extends _i1.SmartFake implements _i13.RepositoryStatus {
- _FakeRepositoryStatus_81(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeCombinedRepositoryStatus_82 extends _i1.SmartFake implements _i13.CombinedRepositoryStatus {
- _FakeCombinedRepositoryStatus_82(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeReleaseNotes_83 extends _i1.SmartFake implements _i13.ReleaseNotes {
- _FakeReleaseNotes_83(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeTableDataInsertAllResponse_84 extends _i1.SmartFake implements _i6.TableDataInsertAllResponse {
- _FakeTableDataInsertAllResponse_84(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeTableDataList_85 extends _i1.SmartFake implements _i6.TableDataList {
- _FakeTableDataList_85(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeUser_86 extends _i1.SmartFake implements _i13.User {
- _FakeUser_86(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeCurrentUser_87 extends _i1.SmartFake implements _i13.CurrentUser {
- _FakeCurrentUser_87(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeEntry_88<T> extends _i1.SmartFake implements _i24.Entry<T> {
- _FakeEntry_88(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeCache_89<T> extends _i1.SmartFake implements _i24.Cache<T> {
- _FakeCache_89(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeAuthentication_90 extends _i1.SmartFake implements _i13.Authentication {
- _FakeAuthentication_90(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeActivityService_91 extends _i1.SmartFake implements _i13.ActivityService {
- _FakeActivityService_91(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeAuthorizationsService_92 extends _i1.SmartFake implements _i13.AuthorizationsService {
- _FakeAuthorizationsService_92(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeGistsService_93 extends _i1.SmartFake implements _i13.GistsService {
- _FakeGistsService_93(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeGitService_94 extends _i1.SmartFake implements _i13.GitService {
- _FakeGitService_94(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeIssuesService_95 extends _i1.SmartFake implements _i13.IssuesService {
- _FakeIssuesService_95(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeMiscService_96 extends _i1.SmartFake implements _i13.MiscService {
- _FakeMiscService_96(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeOrganizationsService_97 extends _i1.SmartFake implements _i13.OrganizationsService {
- _FakeOrganizationsService_97(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakePullRequestsService_98 extends _i1.SmartFake implements _i13.PullRequestsService {
- _FakePullRequestsService_98(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeRepositoriesService_99 extends _i1.SmartFake implements _i13.RepositoriesService {
- _FakeRepositoriesService_99(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeSearchService_100 extends _i1.SmartFake implements _i13.SearchService {
- _FakeSearchService_100(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeUrlShortenerService_101 extends _i1.SmartFake implements _i13.UrlShortenerService {
- _FakeUrlShortenerService_101(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeUsersService_102 extends _i1.SmartFake implements _i13.UsersService {
- _FakeUsersService_102(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeChecksService_103 extends _i1.SmartFake implements _i13.ChecksService {
- _FakeChecksService_103(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeResponse_104 extends _i1.SmartFake implements _i2.Response {
- _FakeResponse_104(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-/// A class which mocks [AccessClientProvider].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockAccessClientProvider extends _i1.Mock implements _i5.AccessClientProvider {
- MockAccessClientProvider() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i19.Future<_i2.Client> createAccessClient(
- {List<String>? scopes = const [r'https://www.googleapis.com/auth/cloud-platform']}) =>
- (super.noSuchMethod(
- Invocation.method(
- #createAccessClient,
- [],
- {#scopes: scopes},
- ),
- returnValue: _i19.Future<_i2.Client>.value(_FakeClient_0(
- this,
- Invocation.method(
- #createAccessClient,
- [],
- {#scopes: scopes},
- ),
- )),
- ) as _i19.Future<_i2.Client>);
-}
-
-/// A class which mocks [AccessTokenService].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockAccessTokenService extends _i1.Mock implements _i25.AccessTokenService {
- MockAccessTokenService() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i3.Config get config => (super.noSuchMethod(
- Invocation.getter(#config),
- returnValue: _FakeConfig_1(
- this,
- Invocation.getter(#config),
- ),
- ) as _i3.Config);
- @override
- _i19.Future<_i4.AccessToken> createAccessToken() => (super.noSuchMethod(
- Invocation.method(
- #createAccessToken,
- [],
- ),
- returnValue: _i19.Future<_i4.AccessToken>.value(_FakeAccessToken_2(
- this,
- Invocation.method(
- #createAccessToken,
- [],
- ),
- )),
- ) as _i19.Future<_i4.AccessToken>);
-}
-
-/// A class which mocks [BigqueryService].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockBigqueryService extends _i1.Mock implements _i15.BigqueryService {
- MockBigqueryService() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i5.AccessClientProvider get accessClientProvider => (super.noSuchMethod(
- Invocation.getter(#accessClientProvider),
- returnValue: _FakeAccessClientProvider_3(
- this,
- Invocation.getter(#accessClientProvider),
- ),
- ) as _i5.AccessClientProvider);
- @override
- _i19.Future<_i6.TabledataResource> defaultTabledata() => (super.noSuchMethod(
- Invocation.method(
- #defaultTabledata,
- [],
- ),
- returnValue: _i19.Future<_i6.TabledataResource>.value(_FakeTabledataResource_4(
- this,
- Invocation.method(
- #defaultTabledata,
- [],
- ),
- )),
- ) as _i19.Future<_i6.TabledataResource>);
- @override
- _i19.Future<_i6.JobsResource> defaultJobs() => (super.noSuchMethod(
- Invocation.method(
- #defaultJobs,
- [],
- ),
- returnValue: _i19.Future<_i6.JobsResource>.value(_FakeJobsResource_5(
- this,
- Invocation.method(
- #defaultJobs,
- [],
- ),
- )),
- ) as _i19.Future<_i6.JobsResource>);
- @override
- _i19.Future<List<_i15.BuilderStatistic>> listBuilderStatistic(
- String? projectId, {
- int? limit = 100,
- String? bucket = r'prod',
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #listBuilderStatistic,
- [projectId],
- {
- #limit: limit,
- #bucket: bucket,
- },
- ),
- returnValue: _i19.Future<List<_i15.BuilderStatistic>>.value(<_i15.BuilderStatistic>[]),
- ) as _i19.Future<List<_i15.BuilderStatistic>>);
- @override
- _i19.Future<List<_i15.BuilderRecord>> listRecentBuildRecordsForBuilder(
- String? projectId, {
- String? builder,
- int? limit,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #listRecentBuildRecordsForBuilder,
- [projectId],
- {
- #builder: builder,
- #limit: limit,
- },
- ),
- returnValue: _i19.Future<List<_i15.BuilderRecord>>.value(<_i15.BuilderRecord>[]),
- ) as _i19.Future<List<_i15.BuilderRecord>>);
-}
-
-/// A class which mocks [BranchService].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockBranchService extends _i1.Mock implements _i23.BranchService {
- MockBranchService() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i3.Config get config => (super.noSuchMethod(
- Invocation.getter(#config),
- returnValue: _FakeConfig_1(
- this,
- Invocation.getter(#config),
- ),
- ) as _i3.Config);
- @override
- _i7.GerritService get gerritService => (super.noSuchMethod(
- Invocation.getter(#gerritService),
- returnValue: _FakeGerritService_6(
- this,
- Invocation.getter(#gerritService),
- ),
- ) as _i7.GerritService);
- @override
- _i26.RetryOptions get retryOptions => (super.noSuchMethod(
- Invocation.getter(#retryOptions),
- returnValue: _i27.dummyValue<_i26.RetryOptions>(
- this,
- Invocation.getter(#retryOptions),
- ),
- ) as _i26.RetryOptions);
- @override
- _i19.Future<void> branchFlutterRecipes(
- String? branch,
- String? engineSha,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #branchFlutterRecipes,
- [
- branch,
- engineSha,
- ],
- ),
- returnValue: _i19.Future<void>.value(),
- returnValueForMissingStub: _i19.Future<void>.value(),
- ) as _i19.Future<void>);
- @override
- _i19.Future<List<Map<String, String>>> getReleaseBranches({
- required _i16.GithubService? githubService,
- required _i13.RepositorySlug? slug,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #getReleaseBranches,
- [],
- {
- #githubService: githubService,
- #slug: slug,
- },
- ),
- returnValue: _i19.Future<List<Map<String, String>>>.value(<Map<String, String>>[]),
- ) as _i19.Future<List<Map<String, String>>>);
-}
-
-/// A class which mocks [BuildBucketClient].
-///
-/// See the documentation for Mockito's code generation for more information.
-// ignore: must_be_immutable
-class MockBuildBucketClient extends _i1.Mock implements _i23.BuildBucketClient {
- MockBuildBucketClient() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- String get buildBucketBuildUri => (super.noSuchMethod(
- Invocation.getter(#buildBucketBuildUri),
- returnValue: '',
- ) as String);
- @override
- String get buildBucketBuilderUri => (super.noSuchMethod(
- Invocation.getter(#buildBucketBuilderUri),
- returnValue: '',
- ) as String);
- @override
- _i2.Client get httpClient => (super.noSuchMethod(
- Invocation.getter(#httpClient),
- returnValue: _FakeClient_0(
- this,
- Invocation.getter(#httpClient),
- ),
- ) as _i2.Client);
- @override
- _i19.Future<_i8.Build> scheduleBuild(
- _i8.ScheduleBuildRequest? request, {
- String? buildBucketUri = r'https://cr-buildbucket.appspot.com/prpc/buildbucket.v2.Builds',
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #scheduleBuild,
- [request],
- {#buildBucketUri: buildBucketUri},
- ),
- returnValue: _i19.Future<_i8.Build>.value(_FakeBuild_7(
- this,
- Invocation.method(
- #scheduleBuild,
- [request],
- {#buildBucketUri: buildBucketUri},
- ),
- )),
- ) as _i19.Future<_i8.Build>);
- @override
- _i19.Future<_i8.SearchBuildsResponse> searchBuilds(
- _i8.SearchBuildsRequest? request, {
- String? buildBucketUri = r'https://cr-buildbucket.appspot.com/prpc/buildbucket.v2.Builds',
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #searchBuilds,
- [request],
- {#buildBucketUri: buildBucketUri},
- ),
- returnValue: _i19.Future<_i8.SearchBuildsResponse>.value(_FakeSearchBuildsResponse_8(
- this,
- Invocation.method(
- #searchBuilds,
- [request],
- {#buildBucketUri: buildBucketUri},
- ),
- )),
- ) as _i19.Future<_i8.SearchBuildsResponse>);
- @override
- _i19.Future<_i8.BatchResponse> batch(
- _i8.BatchRequest? request, {
- String? buildBucketUri = r'https://cr-buildbucket.appspot.com/prpc/buildbucket.v2.Builds',
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #batch,
- [request],
- {#buildBucketUri: buildBucketUri},
- ),
- returnValue: _i19.Future<_i8.BatchResponse>.value(_FakeBatchResponse_9(
- this,
- Invocation.method(
- #batch,
- [request],
- {#buildBucketUri: buildBucketUri},
- ),
- )),
- ) as _i19.Future<_i8.BatchResponse>);
- @override
- _i19.Future<_i8.Build> cancelBuild(
- _i8.CancelBuildRequest? request, {
- String? buildBucketUri = r'https://cr-buildbucket.appspot.com/prpc/buildbucket.v2.Builds',
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #cancelBuild,
- [request],
- {#buildBucketUri: buildBucketUri},
- ),
- returnValue: _i19.Future<_i8.Build>.value(_FakeBuild_7(
- this,
- Invocation.method(
- #cancelBuild,
- [request],
- {#buildBucketUri: buildBucketUri},
- ),
- )),
- ) as _i19.Future<_i8.Build>);
- @override
- _i19.Future<_i8.Build> getBuild(
- _i8.GetBuildRequest? request, {
- String? buildBucketUri = r'https://cr-buildbucket.appspot.com/prpc/buildbucket.v2.Builds',
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #getBuild,
- [request],
- {#buildBucketUri: buildBucketUri},
- ),
- returnValue: _i19.Future<_i8.Build>.value(_FakeBuild_7(
- this,
- Invocation.method(
- #getBuild,
- [request],
- {#buildBucketUri: buildBucketUri},
- ),
- )),
- ) as _i19.Future<_i8.Build>);
- @override
- _i19.Future<_i8.ListBuildersResponse> listBuilders(
- _i8.ListBuildersRequest? request, {
- String? buildBucketUri = r'https://cr-buildbucket.appspot.com/prpc/buildbucket.v2.Builders',
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #listBuilders,
- [request],
- {#buildBucketUri: buildBucketUri},
- ),
- returnValue: _i19.Future<_i8.ListBuildersResponse>.value(_FakeListBuildersResponse_10(
- this,
- Invocation.method(
- #listBuilders,
- [request],
- {#buildBucketUri: buildBucketUri},
- ),
- )),
- ) as _i19.Future<_i8.ListBuildersResponse>);
- @override
- void close() => super.noSuchMethod(
- Invocation.method(
- #close,
- [],
- ),
- returnValueForMissingStub: null,
- );
-}
-
-/// A class which mocks [CommitService].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockCommitService extends _i1.Mock implements _i28.CommitService {
- MockCommitService() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i3.Config get config => (super.noSuchMethod(
- Invocation.getter(#config),
- returnValue: _FakeConfig_1(
- this,
- Invocation.getter(#config),
- ),
- ) as _i3.Config);
- @override
- _i9.DatastoreServiceProvider get datastoreProvider => (super.noSuchMethod(
- Invocation.getter(#datastoreProvider),
- returnValue: (_i11.DatastoreDB db) => _FakeDatastoreService_11(
- this,
- Invocation.getter(#datastoreProvider),
- ),
- ) as _i9.DatastoreServiceProvider);
- @override
- _i19.Future<void> handleCreateGithubRequest(_i29.CreateEvent? createEvent) => (super.noSuchMethod(
- Invocation.method(
- #handleCreateGithubRequest,
- [createEvent],
- ),
- returnValue: _i19.Future<void>.value(),
- returnValueForMissingStub: _i19.Future<void>.value(),
- ) as _i19.Future<void>);
- @override
- _i19.Future<void> handlePushGithubRequest(Map<String, dynamic>? pushEvent) => (super.noSuchMethod(
- Invocation.method(
- #handlePushGithubRequest,
- [pushEvent],
- ),
- returnValue: _i19.Future<void>.value(),
- returnValueForMissingStub: _i19.Future<void>.value(),
- ) as _i19.Future<void>);
-}
-
-/// A class which mocks [Config].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockConfig extends _i1.Mock implements _i3.Config {
- MockConfig() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- Set<_i13.RepositorySlug> get supportedRepos => (super.noSuchMethod(
- Invocation.getter(#supportedRepos),
- returnValue: <_i13.RepositorySlug>{},
- ) as Set<_i13.RepositorySlug>);
- @override
- Set<_i13.RepositorySlug> get postsubmitSupportedRepos => (super.noSuchMethod(
- Invocation.getter(#postsubmitSupportedRepos),
- returnValue: <_i13.RepositorySlug>{},
- ) as Set<_i13.RepositorySlug>);
- @override
- String get autosubmitBot => (super.noSuchMethod(
- Invocation.getter(#autosubmitBot),
- returnValue: '',
- ) as String);
- @override
- _i10.Logging get loggingService => (super.noSuchMethod(
- Invocation.getter(#loggingService),
- returnValue: _FakeLogging_12(
- this,
- Invocation.getter(#loggingService),
- ),
- ) as _i10.Logging);
- @override
- _i19.Future<String> get githubPrivateKey => (super.noSuchMethod(
- Invocation.getter(#githubPrivateKey),
- returnValue: _i19.Future<String>.value(''),
- ) as _i19.Future<String>);
- @override
- _i19.Future<String> get overrideTreeStatusLabel => (super.noSuchMethod(
- Invocation.getter(#overrideTreeStatusLabel),
- returnValue: _i19.Future<String>.value(''),
- ) as _i19.Future<String>);
- @override
- _i19.Future<String> get githubPublicKey => (super.noSuchMethod(
- Invocation.getter(#githubPublicKey),
- returnValue: _i19.Future<String>.value(''),
- ) as _i19.Future<String>);
- @override
- _i19.Future<String> get githubAppId => (super.noSuchMethod(
- Invocation.getter(#githubAppId),
- returnValue: _i19.Future<String>.value(''),
- ) as _i19.Future<String>);
- @override
- _i19.Future<Map<String, dynamic>> get githubAppInstallations => (super.noSuchMethod(
- Invocation.getter(#githubAppInstallations),
- returnValue: _i19.Future<Map<String, dynamic>>.value(<String, dynamic>{}),
- ) as _i19.Future<Map<String, dynamic>>);
- @override
- String get defaultRecipeBundleRef => (super.noSuchMethod(
- Invocation.getter(#defaultRecipeBundleRef),
- returnValue: '',
- ) as String);
- @override
- _i11.DatastoreDB get db => (super.noSuchMethod(
- Invocation.getter(#db),
- returnValue: _FakeDatastoreDB_13(
- this,
- Invocation.getter(#db),
- ),
- ) as _i11.DatastoreDB);
- @override
- int get schedulingShardSize => (super.noSuchMethod(
- Invocation.getter(#schedulingShardSize),
- returnValue: 0,
- ) as int);
- @override
- int get batchSize => (super.noSuchMethod(
- Invocation.getter(#batchSize),
- returnValue: 0,
- ) as int);
- @override
- int get backfillerTargetLimit => (super.noSuchMethod(
- Invocation.getter(#backfillerTargetLimit),
- returnValue: 0,
- ) as int);
- @override
- int get backfillerCommitLimit => (super.noSuchMethod(
- Invocation.getter(#backfillerCommitLimit),
- returnValue: 0,
- ) as int);
- @override
- int get issueAndPRLimit => (super.noSuchMethod(
- Invocation.getter(#issueAndPRLimit),
- returnValue: 0,
- ) as int);
- @override
- _i19.Future<List<String>> get releaseAccounts => (super.noSuchMethod(
- Invocation.getter(#releaseAccounts),
- returnValue: _i19.Future<List<String>>.value(<String>[]),
- ) as _i19.Future<List<String>>);
- @override
- _i19.Future<String> get oauthClientId => (super.noSuchMethod(
- Invocation.getter(#oauthClientId),
- returnValue: _i19.Future<String>.value(''),
- ) as _i19.Future<String>);
- @override
- _i19.Future<String> get frobWebhookKey => (super.noSuchMethod(
- Invocation.getter(#frobWebhookKey),
- returnValue: _i19.Future<String>.value(''),
- ) as _i19.Future<String>);
- @override
- _i19.Future<String> get githubOAuthToken => (super.noSuchMethod(
- Invocation.getter(#githubOAuthToken),
- returnValue: _i19.Future<String>.value(''),
- ) as _i19.Future<String>);
- @override
- String get wrongBaseBranchPullRequestMessage => (super.noSuchMethod(
- Invocation.getter(#wrongBaseBranchPullRequestMessage),
- returnValue: '',
- ) as String);
- @override
- String get releaseBranchPullRequestMessage => (super.noSuchMethod(
- Invocation.getter(#releaseBranchPullRequestMessage),
- returnValue: '',
- ) as String);
- @override
- _i19.Future<String> get webhookKey => (super.noSuchMethod(
- Invocation.getter(#webhookKey),
- returnValue: _i19.Future<String>.value(''),
- ) as _i19.Future<String>);
- @override
- String get mergeConflictPullRequestMessage => (super.noSuchMethod(
- Invocation.getter(#mergeConflictPullRequestMessage),
- returnValue: '',
- ) as String);
- @override
- String get missingTestsPullRequestMessage => (super.noSuchMethod(
- Invocation.getter(#missingTestsPullRequestMessage),
- returnValue: '',
- ) as String);
- @override
- String get flutterGoldPending => (super.noSuchMethod(
- Invocation.getter(#flutterGoldPending),
- returnValue: '',
- ) as String);
- @override
- String get flutterGoldSuccess => (super.noSuchMethod(
- Invocation.getter(#flutterGoldSuccess),
- returnValue: '',
- ) as String);
- @override
- String get flutterGoldChanges => (super.noSuchMethod(
- Invocation.getter(#flutterGoldChanges),
- returnValue: '',
- ) as String);
- @override
- String get flutterGoldStalePR => (super.noSuchMethod(
- Invocation.getter(#flutterGoldStalePR),
- returnValue: '',
- ) as String);
- @override
- String get flutterGoldDraftChange => (super.noSuchMethod(
- Invocation.getter(#flutterGoldDraftChange),
- returnValue: '',
- ) as String);
- @override
- int get maxTaskRetries => (super.noSuchMethod(
- Invocation.getter(#maxTaskRetries),
- returnValue: 0,
- ) as int);
- @override
- int get maxLuciTaskRetries => (super.noSuchMethod(
- Invocation.getter(#maxLuciTaskRetries),
- returnValue: 0,
- ) as int);
- @override
- int get commitNumber => (super.noSuchMethod(
- Invocation.getter(#commitNumber),
- returnValue: 0,
- ) as int);
- @override
- _i12.KeyHelper get keyHelper => (super.noSuchMethod(
- Invocation.getter(#keyHelper),
- returnValue: _FakeKeyHelper_14(
- this,
- Invocation.getter(#keyHelper),
- ),
- ) as _i12.KeyHelper);
- @override
- int get maxRecords => (super.noSuchMethod(
- Invocation.getter(#maxRecords),
- returnValue: 0,
- ) as int);
- @override
- Duration get githubRequestDelay => (super.noSuchMethod(
- Invocation.getter(#githubRequestDelay),
- returnValue: _FakeDuration_15(
- this,
- Invocation.getter(#githubRequestDelay),
- ),
- ) as Duration);
- @override
- String get flutterBuild => (super.noSuchMethod(
- Invocation.getter(#flutterBuild),
- returnValue: '',
- ) as String);
- @override
- String get flutterBuildDescription => (super.noSuchMethod(
- Invocation.getter(#flutterBuildDescription),
- returnValue: '',
- ) as String);
- @override
- String get waitingForTreeToGoGreenLabelName => (super.noSuchMethod(
- Invocation.getter(#waitingForTreeToGoGreenLabelName),
- returnValue: '',
- ) as String);
- @override
- Set<String> get rollerAccounts => (super.noSuchMethod(
- Invocation.getter(#rollerAccounts),
- returnValue: <String>{},
- ) as Set<String>);
- @override
- _i19.Future<Iterable<_i30.Branch>> getBranches(_i13.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #getBranches,
- [slug],
- ),
- returnValue: _i19.Future<Iterable<_i30.Branch>>.value(<_i30.Branch>[]),
- ) as _i19.Future<Iterable<_i30.Branch>>);
- @override
- String wrongHeadBranchPullRequestMessage(String? branch) => (super.noSuchMethod(
- Invocation.method(
- #wrongHeadBranchPullRequestMessage,
- [branch],
- ),
- returnValue: '',
- ) as String);
- @override
- String flutterGoldInitialAlert(String? url) => (super.noSuchMethod(
- Invocation.method(
- #flutterGoldInitialAlert,
- [url],
- ),
- returnValue: '',
- ) as String);
- @override
- String flutterGoldFollowUpAlert(String? url) => (super.noSuchMethod(
- Invocation.method(
- #flutterGoldFollowUpAlert,
- [url],
- ),
- returnValue: '',
- ) as String);
- @override
- String flutterGoldAlertConstant(_i13.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #flutterGoldAlertConstant,
- [slug],
- ),
- returnValue: '',
- ) as String);
- @override
- String flutterGoldCommentID(_i13.PullRequest? pr) => (super.noSuchMethod(
- Invocation.method(
- #flutterGoldCommentID,
- [pr],
- ),
- returnValue: '',
- ) as String);
- @override
- _i19.Future<String> generateJsonWebToken() => (super.noSuchMethod(
- Invocation.method(
- #generateJsonWebToken,
- [],
- ),
- returnValue: _i19.Future<String>.value(''),
- ) as _i19.Future<String>);
- @override
- _i19.Future<String> generateGithubToken(_i13.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #generateGithubToken,
- [slug],
- ),
- returnValue: _i19.Future<String>.value(''),
- ) as _i19.Future<String>);
- @override
- _i19.Future<_i13.GitHub> createGitHubClient({
- _i13.PullRequest? pullRequest,
- _i13.RepositorySlug? slug,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #createGitHubClient,
- [],
- {
- #pullRequest: pullRequest,
- #slug: slug,
- },
- ),
- returnValue: _i19.Future<_i13.GitHub>.value(_FakeGitHub_16(
- this,
- Invocation.method(
- #createGitHubClient,
- [],
- {
- #pullRequest: pullRequest,
- #slug: slug,
- },
- ),
- )),
- ) as _i19.Future<_i13.GitHub>);
- @override
- _i13.GitHub createGitHubClientWithToken(String? token) => (super.noSuchMethod(
- Invocation.method(
- #createGitHubClientWithToken,
- [token],
- ),
- returnValue: _FakeGitHub_16(
- this,
- Invocation.method(
- #createGitHubClientWithToken,
- [token],
- ),
- ),
- ) as _i13.GitHub);
- @override
- _i19.Future<_i14.GraphQLClient> createGitHubGraphQLClient() => (super.noSuchMethod(
- Invocation.method(
- #createGitHubGraphQLClient,
- [],
- ),
- returnValue: _i19.Future<_i14.GraphQLClient>.value(_FakeGraphQLClient_17(
- this,
- Invocation.method(
- #createGitHubGraphQLClient,
- [],
- ),
- )),
- ) as _i19.Future<_i14.GraphQLClient>);
- @override
- _i19.Future<_i15.BigqueryService> createBigQueryService() => (super.noSuchMethod(
- Invocation.method(
- #createBigQueryService,
- [],
- ),
- returnValue: _i19.Future<_i15.BigqueryService>.value(_FakeBigqueryService_18(
- this,
- Invocation.method(
- #createBigQueryService,
- [],
- ),
- )),
- ) as _i19.Future<_i15.BigqueryService>);
- @override
- _i19.Future<_i6.TabledataResource> createTabledataResourceApi() => (super.noSuchMethod(
- Invocation.method(
- #createTabledataResourceApi,
- [],
- ),
- returnValue: _i19.Future<_i6.TabledataResource>.value(_FakeTabledataResource_4(
- this,
- Invocation.method(
- #createTabledataResourceApi,
- [],
- ),
- )),
- ) as _i19.Future<_i6.TabledataResource>);
- @override
- _i19.Future<_i16.GithubService> createDefaultGitHubService() => (super.noSuchMethod(
- Invocation.method(
- #createDefaultGitHubService,
- [],
- ),
- returnValue: _i19.Future<_i16.GithubService>.value(_FakeGithubService_19(
- this,
- Invocation.method(
- #createDefaultGitHubService,
- [],
- ),
- )),
- ) as _i19.Future<_i16.GithubService>);
- @override
- _i19.Future<_i16.GithubService> createGithubService(_i13.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #createGithubService,
- [slug],
- ),
- returnValue: _i19.Future<_i16.GithubService>.value(_FakeGithubService_19(
- this,
- Invocation.method(
- #createGithubService,
- [slug],
- ),
- )),
- ) as _i19.Future<_i16.GithubService>);
- @override
- _i16.GithubService createGithubServiceWithToken(String? token) => (super.noSuchMethod(
- Invocation.method(
- #createGithubServiceWithToken,
- [token],
- ),
- returnValue: _FakeGithubService_19(
- this,
- Invocation.method(
- #createGithubServiceWithToken,
- [token],
- ),
- ),
- ) as _i16.GithubService);
-}
-
-/// A class which mocks [DatastoreService].
-///
-/// See the documentation for Mockito's code generation for more information.
-// ignore: must_be_immutable
-class MockDatastoreService extends _i1.Mock implements _i9.DatastoreService {
- MockDatastoreService() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- int get maxEntityGroups => (super.noSuchMethod(
- Invocation.getter(#maxEntityGroups),
- returnValue: 0,
- ) as int);
- @override
- _i11.DatastoreDB get db => (super.noSuchMethod(
- Invocation.getter(#db),
- returnValue: _FakeDatastoreDB_13(
- this,
- Invocation.getter(#db),
- ),
- ) as _i11.DatastoreDB);
- @override
- _i26.RetryOptions get retryOptions => (super.noSuchMethod(
- Invocation.getter(#retryOptions),
- returnValue: _i27.dummyValue<_i26.RetryOptions>(
- this,
- Invocation.getter(#retryOptions),
- ),
- ) as _i26.RetryOptions);
- @override
- _i19.Stream<_i31.Commit> queryRecentCommits({
- int? limit = 100,
- int? timestamp,
- String? branch,
- required _i13.RepositorySlug? slug,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #queryRecentCommits,
- [],
- {
- #limit: limit,
- #timestamp: timestamp,
- #branch: branch,
- #slug: slug,
- },
- ),
- returnValue: _i19.Stream<_i31.Commit>.empty(),
- ) as _i19.Stream<_i31.Commit>);
- @override
- _i19.Stream<_i30.Branch> queryBranches() => (super.noSuchMethod(
- Invocation.method(
- #queryBranches,
- [],
- ),
- returnValue: _i19.Stream<_i30.Branch>.empty(),
- ) as _i19.Stream<_i30.Branch>);
- @override
- _i19.Stream<_i32.Task> queryRecentTasksByName({
- int? limit = 100,
- required String? name,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #queryRecentTasksByName,
- [],
- {
- #limit: limit,
- #name: name,
- },
- ),
- returnValue: _i19.Stream<_i32.Task>.empty(),
- ) as _i19.Stream<_i32.Task>);
- @override
- _i19.Stream<_i32.FullTask> queryRecentTasks({
- String? taskName,
- int? commitLimit = 20,
- String? branch,
- required _i13.RepositorySlug? slug,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #queryRecentTasks,
- [],
- {
- #taskName: taskName,
- #commitLimit: commitLimit,
- #branch: branch,
- #slug: slug,
- },
- ),
- returnValue: _i19.Stream<_i32.FullTask>.empty(),
- ) as _i19.Stream<_i32.FullTask>);
- @override
- _i19.Future<List<_i33.Stage>> queryTasksGroupedByStage(_i31.Commit? commit) => (super.noSuchMethod(
- Invocation.method(
- #queryTasksGroupedByStage,
- [commit],
- ),
- returnValue: _i19.Future<List<_i33.Stage>>.value(<_i33.Stage>[]),
- ) as _i19.Future<List<_i33.Stage>>);
- @override
- _i19.Future<_i17.GithubBuildStatusUpdate> queryLastStatusUpdate(
- _i13.RepositorySlug? slug,
- _i13.PullRequest? pr,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #queryLastStatusUpdate,
- [
- slug,
- pr,
- ],
- ),
- returnValue: _i19.Future<_i17.GithubBuildStatusUpdate>.value(_FakeGithubBuildStatusUpdate_20(
- this,
- Invocation.method(
- #queryLastStatusUpdate,
- [
- slug,
- pr,
- ],
- ),
- )),
- ) as _i19.Future<_i17.GithubBuildStatusUpdate>);
- @override
- _i19.Future<_i18.GithubGoldStatusUpdate> queryLastGoldUpdate(
- _i13.RepositorySlug? slug,
- _i13.PullRequest? pr,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #queryLastGoldUpdate,
- [
- slug,
- pr,
- ],
- ),
- returnValue: _i19.Future<_i18.GithubGoldStatusUpdate>.value(_FakeGithubGoldStatusUpdate_21(
- this,
- Invocation.method(
- #queryLastGoldUpdate,
- [
- slug,
- pr,
- ],
- ),
- )),
- ) as _i19.Future<_i18.GithubGoldStatusUpdate>);
- @override
- _i19.Future<List<List<_i11.Model<dynamic>>>> shard(List<_i11.Model<dynamic>>? rows) => (super.noSuchMethod(
- Invocation.method(
- #shard,
- [rows],
- ),
- returnValue: _i19.Future<List<List<_i11.Model<dynamic>>>>.value(<List<_i11.Model<dynamic>>>[]),
- ) as _i19.Future<List<List<_i11.Model<dynamic>>>>);
- @override
- _i19.Future<void> insert(List<_i11.Model<dynamic>>? rows) => (super.noSuchMethod(
- Invocation.method(
- #insert,
- [rows],
- ),
- returnValue: _i19.Future<void>.value(),
- returnValueForMissingStub: _i19.Future<void>.value(),
- ) as _i19.Future<void>);
- @override
- _i19.Future<List<T?>> lookupByKey<T extends _i11.Model<dynamic>>(List<_i11.Key<dynamic>>? keys) =>
- (super.noSuchMethod(
- Invocation.method(
- #lookupByKey,
- [keys],
- ),
- returnValue: _i19.Future<List<T?>>.value(<T?>[]),
- ) as _i19.Future<List<T?>>);
- @override
- _i19.Future<T> lookupByValue<T extends _i11.Model<dynamic>>(
- _i11.Key<dynamic>? key, {
- T Function()? orElse,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #lookupByValue,
- [key],
- {#orElse: orElse},
- ),
- returnValue: _i27.ifNotNull(
- _i27.dummyValueOrNull<T>(
- this,
- Invocation.method(
- #lookupByValue,
- [key],
- {#orElse: orElse},
- ),
- ),
- (T v) => _i19.Future<T>.value(v),
- ) ??
- _FakeFuture_22<T>(
- this,
- Invocation.method(
- #lookupByValue,
- [key],
- {#orElse: orElse},
- ),
- ),
- ) as _i19.Future<T>);
- @override
- _i19.Future<T?> withTransaction<T>(_i19.Future<T> Function(_i11.Transaction)? handler) => (super.noSuchMethod(
- Invocation.method(
- #withTransaction,
- [handler],
- ),
- returnValue: _i19.Future<T?>.value(),
- ) as _i19.Future<T?>);
- @override
- _i19.Future<_i32.Task?> getTaskFromBuildbucketBuild(
- _i8.Build? build, {
- String? customName,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #getTaskFromBuildbucketBuild,
- [build],
- {#customName: customName},
- ),
- returnValue: _i19.Future<_i32.Task?>.value(),
- ) as _i19.Future<_i32.Task?>);
-}
-
-/// A class which mocks [FakeEntry].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockFakeEntry extends _i1.Mock implements _i34.FakeEntry {
- MockFakeEntry() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i35.Uint8List get value => (super.noSuchMethod(
- Invocation.getter(#value),
- returnValue: _i35.Uint8List(0),
- ) as _i35.Uint8List);
- @override
- set value(_i35.Uint8List? _value) => super.noSuchMethod(
- Invocation.setter(
- #value,
- _value,
- ),
- returnValueForMissingStub: null,
- );
- @override
- _i19.Future<_i35.Uint8List> get([
- _i19.Future<_i35.Uint8List?> Function()? create,
- Duration? ttl,
- ]) =>
- (super.noSuchMethod(
- Invocation.method(
- #get,
- [
- create,
- ttl,
- ],
- ),
- returnValue: _i19.Future<_i35.Uint8List>.value(_i35.Uint8List(0)),
- ) as _i19.Future<_i35.Uint8List>);
- @override
- _i19.Future<void> purge({int? retries = 0}) => (super.noSuchMethod(
- Invocation.method(
- #purge,
- [],
- {#retries: retries},
- ),
- returnValue: _i19.Future<void>.value(),
- returnValueForMissingStub: _i19.Future<void>.value(),
- ) as _i19.Future<void>);
- @override
- _i19.Future<_i35.Uint8List?> set(
- _i35.Uint8List? value, [
- Duration? ttl,
- ]) =>
- (super.noSuchMethod(
- Invocation.method(
- #set,
- [
- value,
- ttl,
- ],
- ),
- returnValue: _i19.Future<_i35.Uint8List?>.value(),
- ) as _i19.Future<_i35.Uint8List?>);
-}
-
-/// A class which mocks [IssuesService].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockIssuesService extends _i1.Mock implements _i13.IssuesService {
- MockIssuesService() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i13.GitHub get github => (super.noSuchMethod(
- Invocation.getter(#github),
- returnValue: _FakeGitHub_16(
- this,
- Invocation.getter(#github),
- ),
- ) as _i13.GitHub);
- @override
- _i19.Stream<_i13.Issue> listAll({
- int? milestoneNumber,
- String? state,
- String? direction,
- String? sort,
- DateTime? since,
- int? perPage,
- List<String>? labels,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #listAll,
- [],
- {
- #milestoneNumber: milestoneNumber,
- #state: state,
- #direction: direction,
- #sort: sort,
- #since: since,
- #perPage: perPage,
- #labels: labels,
- },
- ),
- returnValue: _i19.Stream<_i13.Issue>.empty(),
- ) as _i19.Stream<_i13.Issue>);
- @override
- _i19.Stream<_i13.Issue> listByUser({
- int? milestoneNumber,
- String? state,
- String? direction,
- String? sort,
- DateTime? since,
- int? perPage,
- List<String>? labels,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #listByUser,
- [],
- {
- #milestoneNumber: milestoneNumber,
- #state: state,
- #direction: direction,
- #sort: sort,
- #since: since,
- #perPage: perPage,
- #labels: labels,
- },
- ),
- returnValue: _i19.Stream<_i13.Issue>.empty(),
- ) as _i19.Stream<_i13.Issue>);
- @override
- _i19.Stream<_i13.Issue> listByOrg(
- String? org, {
- int? milestoneNumber,
- String? state,
- String? direction,
- String? sort,
- DateTime? since,
- int? perPage,
- List<String>? labels,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #listByOrg,
- [org],
- {
- #milestoneNumber: milestoneNumber,
- #state: state,
- #direction: direction,
- #sort: sort,
- #since: since,
- #perPage: perPage,
- #labels: labels,
- },
- ),
- returnValue: _i19.Stream<_i13.Issue>.empty(),
- ) as _i19.Stream<_i13.Issue>);
- @override
- _i19.Stream<_i13.Issue> listByRepo(
- _i13.RepositorySlug? slug, {
- int? milestoneNumber,
- String? state,
- String? direction,
- String? sort,
- DateTime? since,
- int? perPage,
- List<String>? labels,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #listByRepo,
- [slug],
- {
- #milestoneNumber: milestoneNumber,
- #state: state,
- #direction: direction,
- #sort: sort,
- #since: since,
- #perPage: perPage,
- #labels: labels,
- },
- ),
- returnValue: _i19.Stream<_i13.Issue>.empty(),
- ) as _i19.Stream<_i13.Issue>);
- @override
- _i19.Stream<_i13.Reaction> listReactions(
- _i13.RepositorySlug? slug,
- int? issueNumber, {
- _i13.ReactionType? content,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #listReactions,
- [
- slug,
- issueNumber,
- ],
- {#content: content},
- ),
- returnValue: _i19.Stream<_i13.Reaction>.empty(),
- ) as _i19.Stream<_i13.Reaction>);
- @override
- _i19.Future<_i13.Issue> edit(
- _i13.RepositorySlug? slug,
- int? issueNumber,
- _i13.IssueRequest? issue,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #edit,
- [
- slug,
- issueNumber,
- issue,
- ],
- ),
- returnValue: _i19.Future<_i13.Issue>.value(_FakeIssue_23(
- this,
- Invocation.method(
- #edit,
- [
- slug,
- issueNumber,
- issue,
- ],
- ),
- )),
- ) as _i19.Future<_i13.Issue>);
- @override
- _i19.Future<_i13.Issue> get(
- _i13.RepositorySlug? slug,
- int? issueNumber,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #get,
- [
- slug,
- issueNumber,
- ],
- ),
- returnValue: _i19.Future<_i13.Issue>.value(_FakeIssue_23(
- this,
- Invocation.method(
- #get,
- [
- slug,
- issueNumber,
- ],
- ),
- )),
- ) as _i19.Future<_i13.Issue>);
- @override
- _i19.Future<_i13.Issue> create(
- _i13.RepositorySlug? slug,
- _i13.IssueRequest? issue,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #create,
- [
- slug,
- issue,
- ],
- ),
- returnValue: _i19.Future<_i13.Issue>.value(_FakeIssue_23(
- this,
- Invocation.method(
- #create,
- [
- slug,
- issue,
- ],
- ),
- )),
- ) as _i19.Future<_i13.Issue>);
- @override
- _i19.Stream<_i13.User> listAssignees(_i13.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #listAssignees,
- [slug],
- ),
- returnValue: _i19.Stream<_i13.User>.empty(),
- ) as _i19.Stream<_i13.User>);
- @override
- _i19.Future<bool> isAssignee(
- _i13.RepositorySlug? slug,
- String? repoName,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #isAssignee,
- [
- slug,
- repoName,
- ],
- ),
- returnValue: _i19.Future<bool>.value(false),
- ) as _i19.Future<bool>);
- @override
- _i19.Stream<_i13.IssueComment> listCommentsByIssue(
- _i13.RepositorySlug? slug,
- int? issueNumber,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #listCommentsByIssue,
- [
- slug,
- issueNumber,
- ],
- ),
- returnValue: _i19.Stream<_i13.IssueComment>.empty(),
- ) as _i19.Stream<_i13.IssueComment>);
- @override
- _i19.Stream<_i13.IssueComment> listCommentsByRepo(_i13.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #listCommentsByRepo,
- [slug],
- ),
- returnValue: _i19.Stream<_i13.IssueComment>.empty(),
- ) as _i19.Stream<_i13.IssueComment>);
- @override
- _i19.Future<_i13.IssueComment> getComment(
- _i13.RepositorySlug? slug,
- int? id,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #getComment,
- [
- slug,
- id,
- ],
- ),
- returnValue: _i19.Future<_i13.IssueComment>.value(_FakeIssueComment_24(
- this,
- Invocation.method(
- #getComment,
- [
- slug,
- id,
- ],
- ),
- )),
- ) as _i19.Future<_i13.IssueComment>);
- @override
- _i19.Future<_i13.IssueComment> createComment(
- _i13.RepositorySlug? slug,
- int? issueNumber,
- String? body,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #createComment,
- [
- slug,
- issueNumber,
- body,
- ],
- ),
- returnValue: _i19.Future<_i13.IssueComment>.value(_FakeIssueComment_24(
- this,
- Invocation.method(
- #createComment,
- [
- slug,
- issueNumber,
- body,
- ],
- ),
- )),
- ) as _i19.Future<_i13.IssueComment>);
- @override
- _i19.Future<_i13.IssueComment> updateComment(
- _i13.RepositorySlug? slug,
- int? id,
- String? body,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #updateComment,
- [
- slug,
- id,
- body,
- ],
- ),
- returnValue: _i19.Future<_i13.IssueComment>.value(_FakeIssueComment_24(
- this,
- Invocation.method(
- #updateComment,
- [
- slug,
- id,
- body,
- ],
- ),
- )),
- ) as _i19.Future<_i13.IssueComment>);
- @override
- _i19.Future<bool> deleteComment(
- _i13.RepositorySlug? slug,
- int? id,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #deleteComment,
- [
- slug,
- id,
- ],
- ),
- returnValue: _i19.Future<bool>.value(false),
- ) as _i19.Future<bool>);
- @override
- _i19.Stream<_i13.IssueLabel> listLabels(_i13.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #listLabels,
- [slug],
- ),
- returnValue: _i19.Stream<_i13.IssueLabel>.empty(),
- ) as _i19.Stream<_i13.IssueLabel>);
- @override
- _i19.Future<_i13.IssueLabel> getLabel(
- _i13.RepositorySlug? slug,
- String? name,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #getLabel,
- [
- slug,
- name,
- ],
- ),
- returnValue: _i19.Future<_i13.IssueLabel>.value(_FakeIssueLabel_25(
- this,
- Invocation.method(
- #getLabel,
- [
- slug,
- name,
- ],
- ),
- )),
- ) as _i19.Future<_i13.IssueLabel>);
- @override
- _i19.Future<_i13.IssueLabel> createLabel(
- _i13.RepositorySlug? slug,
- String? name, {
- String? color,
- String? description,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #createLabel,
- [
- slug,
- name,
- ],
- {
- #color: color,
- #description: description,
- },
- ),
- returnValue: _i19.Future<_i13.IssueLabel>.value(_FakeIssueLabel_25(
- this,
- Invocation.method(
- #createLabel,
- [
- slug,
- name,
- ],
- {
- #color: color,
- #description: description,
- },
- ),
- )),
- ) as _i19.Future<_i13.IssueLabel>);
- @override
- _i19.Future<_i13.IssueLabel> editLabel(
- _i13.RepositorySlug? slug,
- String? name,
- String? color,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #editLabel,
- [
- slug,
- name,
- color,
- ],
- ),
- returnValue: _i19.Future<_i13.IssueLabel>.value(_FakeIssueLabel_25(
- this,
- Invocation.method(
- #editLabel,
- [
- slug,
- name,
- color,
- ],
- ),
- )),
- ) as _i19.Future<_i13.IssueLabel>);
- @override
- _i19.Future<_i13.IssueLabel> updateLabel(
- _i13.RepositorySlug? slug,
- String? name, {
- String? newName,
- String? color,
- String? description,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #updateLabel,
- [
- slug,
- name,
- ],
- {
- #newName: newName,
- #color: color,
- #description: description,
- },
- ),
- returnValue: _i19.Future<_i13.IssueLabel>.value(_FakeIssueLabel_25(
- this,
- Invocation.method(
- #updateLabel,
- [
- slug,
- name,
- ],
- {
- #newName: newName,
- #color: color,
- #description: description,
- },
- ),
- )),
- ) as _i19.Future<_i13.IssueLabel>);
- @override
- _i19.Future<bool> deleteLabel(
- _i13.RepositorySlug? slug,
- String? name,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #deleteLabel,
- [
- slug,
- name,
- ],
- ),
- returnValue: _i19.Future<bool>.value(false),
- ) as _i19.Future<bool>);
- @override
- _i19.Stream<_i13.IssueLabel> listLabelsByIssue(
- _i13.RepositorySlug? slug,
- int? issueNumber,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #listLabelsByIssue,
- [
- slug,
- issueNumber,
- ],
- ),
- returnValue: _i19.Stream<_i13.IssueLabel>.empty(),
- ) as _i19.Stream<_i13.IssueLabel>);
- @override
- _i19.Future<List<_i13.IssueLabel>> addLabelsToIssue(
- _i13.RepositorySlug? slug,
- int? issueNumber,
- List<String>? labels,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #addLabelsToIssue,
- [
- slug,
- issueNumber,
- labels,
- ],
- ),
- returnValue: _i19.Future<List<_i13.IssueLabel>>.value(<_i13.IssueLabel>[]),
- ) as _i19.Future<List<_i13.IssueLabel>>);
- @override
- _i19.Future<List<_i13.IssueLabel>> replaceLabelsForIssue(
- _i13.RepositorySlug? slug,
- int? issueNumber,
- List<String>? labels,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #replaceLabelsForIssue,
- [
- slug,
- issueNumber,
- labels,
- ],
- ),
- returnValue: _i19.Future<List<_i13.IssueLabel>>.value(<_i13.IssueLabel>[]),
- ) as _i19.Future<List<_i13.IssueLabel>>);
- @override
- _i19.Future<bool> removeLabelForIssue(
- _i13.RepositorySlug? slug,
- int? issueNumber,
- String? label,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #removeLabelForIssue,
- [
- slug,
- issueNumber,
- label,
- ],
- ),
- returnValue: _i19.Future<bool>.value(false),
- ) as _i19.Future<bool>);
- @override
- _i19.Future<bool> removeAllLabelsForIssue(
- _i13.RepositorySlug? slug,
- int? issueNumber,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #removeAllLabelsForIssue,
- [
- slug,
- issueNumber,
- ],
- ),
- returnValue: _i19.Future<bool>.value(false),
- ) as _i19.Future<bool>);
- @override
- _i19.Stream<_i13.Milestone> listMilestones(_i13.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #listMilestones,
- [slug],
- ),
- returnValue: _i19.Stream<_i13.Milestone>.empty(),
- ) as _i19.Stream<_i13.Milestone>);
- @override
- _i19.Future<_i13.Milestone> createMilestone(
- _i13.RepositorySlug? slug,
- _i13.CreateMilestone? request,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #createMilestone,
- [
- slug,
- request,
- ],
- ),
- returnValue: _i19.Future<_i13.Milestone>.value(_FakeMilestone_26(
- this,
- Invocation.method(
- #createMilestone,
- [
- slug,
- request,
- ],
- ),
- )),
- ) as _i19.Future<_i13.Milestone>);
- @override
- _i19.Future<bool> deleteMilestone(
- _i13.RepositorySlug? slug,
- int? number,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #deleteMilestone,
- [
- slug,
- number,
- ],
- ),
- returnValue: _i19.Future<bool>.value(false),
- ) as _i19.Future<bool>);
- @override
- _i19.Stream<_i13.TimelineEvent> listTimeline(
- _i13.RepositorySlug? slug,
- int? issueNumber,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #listTimeline,
- [
- slug,
- issueNumber,
- ],
- ),
- returnValue: _i19.Stream<_i13.TimelineEvent>.empty(),
- ) as _i19.Stream<_i13.TimelineEvent>);
- @override
- _i19.Future<void> lock(
- _i13.RepositorySlug? slug,
- int? number, {
- String? lockReason,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #lock,
- [
- slug,
- number,
- ],
- {#lockReason: lockReason},
- ),
- returnValue: _i19.Future<void>.value(),
- returnValueForMissingStub: _i19.Future<void>.value(),
- ) as _i19.Future<void>);
- @override
- _i19.Future<void> unlock(
- _i13.RepositorySlug? slug,
- int? number,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #unlock,
- [
- slug,
- number,
- ],
- ),
- returnValue: _i19.Future<void>.value(),
- returnValueForMissingStub: _i19.Future<void>.value(),
- ) as _i19.Future<void>);
-}
-
-/// A class which mocks [GithubChecksService].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockGithubChecksService extends _i1.Mock implements _i23.GithubChecksService {
- MockGithubChecksService() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i3.Config get config => (super.noSuchMethod(
- Invocation.getter(#config),
- returnValue: _FakeConfig_1(
- this,
- Invocation.getter(#config),
- ),
- ) as _i3.Config);
- @override
- set config(_i3.Config? _config) => super.noSuchMethod(
- Invocation.setter(
- #config,
- _config,
- ),
- returnValueForMissingStub: null,
- );
- @override
- _i20.GithubChecksUtil get githubChecksUtil => (super.noSuchMethod(
- Invocation.getter(#githubChecksUtil),
- returnValue: _FakeGithubChecksUtil_27(
- this,
- Invocation.getter(#githubChecksUtil),
- ),
- ) as _i20.GithubChecksUtil);
- @override
- set githubChecksUtil(_i20.GithubChecksUtil? _githubChecksUtil) => super.noSuchMethod(
- Invocation.setter(
- #githubChecksUtil,
- _githubChecksUtil,
- ),
- returnValueForMissingStub: null,
- );
- @override
- _i19.Future<void> handleCheckSuite(
- _i13.PullRequest? pullRequest,
- _i29.CheckSuiteEvent? checkSuiteEvent,
- _i23.Scheduler? scheduler,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #handleCheckSuite,
- [
- pullRequest,
- checkSuiteEvent,
- scheduler,
- ],
- ),
- returnValue: _i19.Future<void>.value(),
- returnValueForMissingStub: _i19.Future<void>.value(),
- ) as _i19.Future<void>);
- @override
- _i19.Future<bool> updateCheckStatus(
- _i36.BuildPushMessage? buildPushMessage,
- _i23.LuciBuildService? luciBuildService,
- _i13.RepositorySlug? slug, {
- bool? rescheduled = false,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #updateCheckStatus,
- [
- buildPushMessage,
- luciBuildService,
- slug,
- ],
- {#rescheduled: rescheduled},
- ),
- returnValue: _i19.Future<bool>.value(false),
- ) as _i19.Future<bool>);
- @override
- bool taskFailed(_i36.BuildPushMessage? buildPushMessage) => (super.noSuchMethod(
- Invocation.method(
- #taskFailed,
- [buildPushMessage],
- ),
- returnValue: false,
- ) as bool);
- @override
- int currentAttempt(_i36.BuildPushMessage? buildPushMessage) => (super.noSuchMethod(
- Invocation.method(
- #currentAttempt,
- [buildPushMessage],
- ),
- returnValue: 0,
- ) as int);
- @override
- String getGithubSummary(String? summary) => (super.noSuchMethod(
- Invocation.method(
- #getGithubSummary,
- [summary],
- ),
- returnValue: '',
- ) as String);
- @override
- _i13.CheckRunConclusion conclusionForResult(_i36.Result? result) => (super.noSuchMethod(
- Invocation.method(
- #conclusionForResult,
- [result],
- ),
- returnValue: _FakeCheckRunConclusion_28(
- this,
- Invocation.method(
- #conclusionForResult,
- [result],
- ),
- ),
- ) as _i13.CheckRunConclusion);
- @override
- _i13.CheckRunStatus statusForResult(_i36.Status? status) => (super.noSuchMethod(
- Invocation.method(
- #statusForResult,
- [status],
- ),
- returnValue: _FakeCheckRunStatus_29(
- this,
- Invocation.method(
- #statusForResult,
- [status],
- ),
- ),
- ) as _i13.CheckRunStatus);
- @override
- _i19.Future<_i13.PullRequest?> findMatchingPullRequest(
- _i13.RepositorySlug? slug,
- String? headSha,
- int? checkSuiteId,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #findMatchingPullRequest,
- [
- slug,
- headSha,
- checkSuiteId,
- ],
- ),
- returnValue: _i19.Future<_i13.PullRequest?>.value(),
- ) as _i19.Future<_i13.PullRequest?>);
-}
-
-/// A class which mocks [GithubChecksUtil].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockGithubChecksUtil extends _i1.Mock implements _i20.GithubChecksUtil {
- MockGithubChecksUtil() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i19.Future<Map<String, _i13.CheckRun>> allCheckRuns(
- _i13.GitHub? gitHubClient,
- _i29.CheckSuiteEvent? checkSuiteEvent,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #allCheckRuns,
- [
- gitHubClient,
- checkSuiteEvent,
- ],
- ),
- returnValue: _i19.Future<Map<String, _i13.CheckRun>>.value(<String, _i13.CheckRun>{}),
- ) as _i19.Future<Map<String, _i13.CheckRun>>);
- @override
- _i19.Future<_i13.CheckSuite> getCheckSuite(
- _i13.GitHub? gitHubClient,
- _i13.RepositorySlug? slug,
- int? checkSuiteId,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #getCheckSuite,
- [
- gitHubClient,
- slug,
- checkSuiteId,
- ],
- ),
- returnValue: _i19.Future<_i13.CheckSuite>.value(_FakeCheckSuite_30(
- this,
- Invocation.method(
- #getCheckSuite,
- [
- gitHubClient,
- slug,
- checkSuiteId,
- ],
- ),
- )),
- ) as _i19.Future<_i13.CheckSuite>);
- @override
- _i19.Future<List<_i13.CheckSuite>> listCheckSuitesForRef(
- _i13.GitHub? gitHubClient,
- _i13.RepositorySlug? slug, {
- required String? ref,
- int? appId,
- String? checkName,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #listCheckSuitesForRef,
- [
- gitHubClient,
- slug,
- ],
- {
- #ref: ref,
- #appId: appId,
- #checkName: checkName,
- },
- ),
- returnValue: _i19.Future<List<_i13.CheckSuite>>.value(<_i13.CheckSuite>[]),
- ) as _i19.Future<List<_i13.CheckSuite>>);
- @override
- _i19.Future<void> updateCheckRun(
- _i3.Config? config,
- _i13.RepositorySlug? slug,
- _i13.CheckRun? checkRun, {
- _i13.CheckRunStatus? status = _i13.CheckRunStatus.queued,
- _i13.CheckRunConclusion? conclusion,
- String? detailsUrl,
- _i13.CheckRunOutput? output,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #updateCheckRun,
- [
- config,
- slug,
- checkRun,
- ],
- {
- #status: status,
- #conclusion: conclusion,
- #detailsUrl: detailsUrl,
- #output: output,
- },
- ),
- returnValue: _i19.Future<void>.value(),
- returnValueForMissingStub: _i19.Future<void>.value(),
- ) as _i19.Future<void>);
- @override
- _i19.Future<_i13.CheckRun> getCheckRun(
- _i3.Config? config,
- _i13.RepositorySlug? slug,
- int? id,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #getCheckRun,
- [
- config,
- slug,
- id,
- ],
- ),
- returnValue: _i19.Future<_i13.CheckRun>.value(_FakeCheckRun_31(
- this,
- Invocation.method(
- #getCheckRun,
- [
- config,
- slug,
- id,
- ],
- ),
- )),
- ) as _i19.Future<_i13.CheckRun>);
- @override
- _i19.Future<_i13.CheckRun> createCheckRun(
- _i3.Config? config,
- _i13.RepositorySlug? slug,
- String? sha,
- String? name, {
- _i13.CheckRunOutput? output,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #createCheckRun,
- [
- config,
- slug,
- sha,
- name,
- ],
- {#output: output},
- ),
- returnValue: _i19.Future<_i13.CheckRun>.value(_FakeCheckRun_31(
- this,
- Invocation.method(
- #createCheckRun,
- [
- config,
- slug,
- sha,
- name,
- ],
- {#output: output},
- ),
- )),
- ) as _i19.Future<_i13.CheckRun>);
-}
-
-/// A class which mocks [GithubService].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockGithubService extends _i1.Mock implements _i16.GithubService {
- MockGithubService() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i13.GitHub get github => (super.noSuchMethod(
- Invocation.getter(#github),
- returnValue: _FakeGitHub_16(
- this,
- Invocation.getter(#github),
- ),
- ) as _i13.GitHub);
- @override
- _i19.Future<List<_i13.RepositoryCommit>> listBranchedCommits(
- _i13.RepositorySlug? slug,
- String? branch,
- int? lastCommitTimestampMills,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #listBranchedCommits,
- [
- slug,
- branch,
- lastCommitTimestampMills,
- ],
- ),
- returnValue: _i19.Future<List<_i13.RepositoryCommit>>.value(<_i13.RepositoryCommit>[]),
- ) as _i19.Future<List<_i13.RepositoryCommit>>);
- @override
- _i19.Future<List<_i13.PullRequest>> listPullRequests(
- _i13.RepositorySlug? slug,
- String? branch,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #listPullRequests,
- [
- slug,
- branch,
- ],
- ),
- returnValue: _i19.Future<List<_i13.PullRequest>>.value(<_i13.PullRequest>[]),
- ) as _i19.Future<List<_i13.PullRequest>>);
- @override
- _i19.Future<_i13.PullRequest> createPullRequest(
- _i13.RepositorySlug? slug, {
- required String? title,
- String? body,
- String? commitMessage,
- required _i13.GitReference? baseRef,
- List<_i13.CreateGitTreeEntry>? entries,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #createPullRequest,
- [slug],
- {
- #title: title,
- #body: body,
- #commitMessage: commitMessage,
- #baseRef: baseRef,
- #entries: entries,
- },
- ),
- returnValue: _i19.Future<_i13.PullRequest>.value(_FakePullRequest_32(
- this,
- Invocation.method(
- #createPullRequest,
- [slug],
- {
- #title: title,
- #body: body,
- #commitMessage: commitMessage,
- #baseRef: baseRef,
- #entries: entries,
- },
- ),
- )),
- ) as _i19.Future<_i13.PullRequest>);
- @override
- _i19.Future<void> assignReviewer(
- _i13.RepositorySlug? slug, {
- int? pullRequestNumber,
- String? reviewer,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #assignReviewer,
- [slug],
- {
- #pullRequestNumber: pullRequestNumber,
- #reviewer: reviewer,
- },
- ),
- returnValue: _i19.Future<void>.value(),
- returnValueForMissingStub: _i19.Future<void>.value(),
- ) as _i19.Future<void>);
- @override
- _i19.Future<List<_i13.IssueLabel>> addIssueLabels(
- _i13.RepositorySlug? slug,
- int? issueNumber,
- List<String>? labels,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #addIssueLabels,
- [
- slug,
- issueNumber,
- labels,
- ],
- ),
- returnValue: _i19.Future<List<_i13.IssueLabel>>.value(<_i13.IssueLabel>[]),
- ) as _i19.Future<List<_i13.IssueLabel>>);
- @override
- _i19.Future<List<_i13.Issue>> listIssues(
- _i13.RepositorySlug? slug, {
- List<String>? labels,
- String? state = r'open',
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #listIssues,
- [slug],
- {
- #labels: labels,
- #state: state,
- },
- ),
- returnValue: _i19.Future<List<_i13.Issue>>.value(<_i13.Issue>[]),
- ) as _i19.Future<List<_i13.Issue>>);
- @override
- _i19.Future<_i13.Issue>? getIssue(
- _i13.RepositorySlug? slug, {
- required int? issueNumber,
- }) =>
- (super.noSuchMethod(Invocation.method(
- #getIssue,
- [slug],
- {#issueNumber: issueNumber},
- )) as _i19.Future<_i13.Issue>?);
- @override
- _i19.Future<void> assignIssue(
- _i13.RepositorySlug? slug, {
- required int? issueNumber,
- required String? assignee,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #assignIssue,
- [slug],
- {
- #issueNumber: issueNumber,
- #assignee: assignee,
- },
- ),
- returnValue: _i19.Future<void>.value(),
- returnValueForMissingStub: _i19.Future<void>.value(),
- ) as _i19.Future<void>);
- @override
- _i19.Future<_i13.Issue> createIssue(
- _i13.RepositorySlug? slug, {
- String? title,
- String? body,
- List<String>? labels,
- String? assignee,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #createIssue,
- [slug],
- {
- #title: title,
- #body: body,
- #labels: labels,
- #assignee: assignee,
- },
- ),
- returnValue: _i19.Future<_i13.Issue>.value(_FakeIssue_23(
- this,
- Invocation.method(
- #createIssue,
- [slug],
- {
- #title: title,
- #body: body,
- #labels: labels,
- #assignee: assignee,
- },
- ),
- )),
- ) as _i19.Future<_i13.Issue>);
- @override
- _i19.Future<_i13.IssueComment?> createComment(
- _i13.RepositorySlug? slug, {
- required int? issueNumber,
- required String? body,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #createComment,
- [slug],
- {
- #issueNumber: issueNumber,
- #body: body,
- },
- ),
- returnValue: _i19.Future<_i13.IssueComment?>.value(),
- ) as _i19.Future<_i13.IssueComment?>);
- @override
- _i19.Future<List<_i13.IssueLabel>> replaceLabelsForIssue(
- _i13.RepositorySlug? slug, {
- required int? issueNumber,
- required List<String>? labels,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #replaceLabelsForIssue,
- [slug],
- {
- #issueNumber: issueNumber,
- #labels: labels,
- },
- ),
- returnValue: _i19.Future<List<_i13.IssueLabel>>.value(<_i13.IssueLabel>[]),
- ) as _i19.Future<List<_i13.IssueLabel>>);
- @override
- _i19.Future<List<String>> listFiles(_i13.PullRequest? pullRequest) => (super.noSuchMethod(
- Invocation.method(
- #listFiles,
- [pullRequest],
- ),
- returnValue: _i19.Future<List<String>>.value(<String>[]),
- ) as _i19.Future<List<String>>);
- @override
- _i19.Future<String> getFileContent(
- _i13.RepositorySlug? slug,
- String? path, {
- String? ref,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #getFileContent,
- [
- slug,
- path,
- ],
- {#ref: ref},
- ),
- returnValue: _i19.Future<String>.value(''),
- ) as _i19.Future<String>);
- @override
- _i19.Future<_i13.GitReference> getReference(
- _i13.RepositorySlug? slug,
- String? ref,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #getReference,
- [
- slug,
- ref,
- ],
- ),
- returnValue: _i19.Future<_i13.GitReference>.value(_FakeGitReference_33(
- this,
- Invocation.method(
- #getReference,
- [
- slug,
- ref,
- ],
- ),
- )),
- ) as _i19.Future<_i13.GitReference>);
- @override
- _i19.Future<_i13.RateLimit> getRateLimit() => (super.noSuchMethod(
- Invocation.method(
- #getRateLimit,
- [],
- ),
- returnValue: _i19.Future<_i13.RateLimit>.value(_FakeRateLimit_34(
- this,
- Invocation.method(
- #getRateLimit,
- [],
- ),
- )),
- ) as _i19.Future<_i13.RateLimit>);
- @override
- _i19.Future<List<_i13.Issue>> searchIssuesAndPRs(
- _i13.RepositorySlug? slug,
- String? query, {
- String? sort,
- int? pages = 2,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #searchIssuesAndPRs,
- [
- slug,
- query,
- ],
- {
- #sort: sort,
- #pages: pages,
- },
- ),
- returnValue: _i19.Future<List<_i13.Issue>>.value(<_i13.Issue>[]),
- ) as _i19.Future<List<_i13.Issue>>);
- @override
- _i19.Future<_i13.PullRequest> getPullRequest(
- _i13.RepositorySlug? slug,
- int? number,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #getPullRequest,
- [
- slug,
- number,
- ],
- ),
- returnValue: _i19.Future<_i13.PullRequest>.value(_FakePullRequest_32(
- this,
- Invocation.method(
- #getPullRequest,
- [
- slug,
- number,
- ],
- ),
- )),
- ) as _i19.Future<_i13.PullRequest>);
-}
-
-/// A class which mocks [GitService].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockGitService extends _i1.Mock implements _i13.GitService {
- MockGitService() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i13.GitHub get github => (super.noSuchMethod(
- Invocation.getter(#github),
- returnValue: _FakeGitHub_16(
- this,
- Invocation.getter(#github),
- ),
- ) as _i13.GitHub);
- @override
- _i19.Future<_i13.GitBlob> getBlob(
- _i13.RepositorySlug? slug,
- String? sha,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #getBlob,
- [
- slug,
- sha,
- ],
- ),
- returnValue: _i19.Future<_i13.GitBlob>.value(_FakeGitBlob_35(
- this,
- Invocation.method(
- #getBlob,
- [
- slug,
- sha,
- ],
- ),
- )),
- ) as _i19.Future<_i13.GitBlob>);
- @override
- _i19.Future<_i13.GitBlob> createBlob(
- _i13.RepositorySlug? slug,
- _i13.CreateGitBlob? blob,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #createBlob,
- [
- slug,
- blob,
- ],
- ),
- returnValue: _i19.Future<_i13.GitBlob>.value(_FakeGitBlob_35(
- this,
- Invocation.method(
- #createBlob,
- [
- slug,
- blob,
- ],
- ),
- )),
- ) as _i19.Future<_i13.GitBlob>);
- @override
- _i19.Future<_i13.GitCommit> getCommit(
- _i13.RepositorySlug? slug,
- String? sha,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #getCommit,
- [
- slug,
- sha,
- ],
- ),
- returnValue: _i19.Future<_i13.GitCommit>.value(_FakeGitCommit_36(
- this,
- Invocation.method(
- #getCommit,
- [
- slug,
- sha,
- ],
- ),
- )),
- ) as _i19.Future<_i13.GitCommit>);
- @override
- _i19.Future<_i13.GitCommit> createCommit(
- _i13.RepositorySlug? slug,
- _i13.CreateGitCommit? commit,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #createCommit,
- [
- slug,
- commit,
- ],
- ),
- returnValue: _i19.Future<_i13.GitCommit>.value(_FakeGitCommit_36(
- this,
- Invocation.method(
- #createCommit,
- [
- slug,
- commit,
- ],
- ),
- )),
- ) as _i19.Future<_i13.GitCommit>);
- @override
- _i19.Future<_i13.GitReference> getReference(
- _i13.RepositorySlug? slug,
- String? ref,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #getReference,
- [
- slug,
- ref,
- ],
- ),
- returnValue: _i19.Future<_i13.GitReference>.value(_FakeGitReference_33(
- this,
- Invocation.method(
- #getReference,
- [
- slug,
- ref,
- ],
- ),
- )),
- ) as _i19.Future<_i13.GitReference>);
- @override
- _i19.Stream<_i13.GitReference> listReferences(
- _i13.RepositorySlug? slug, {
- String? type,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #listReferences,
- [slug],
- {#type: type},
- ),
- returnValue: _i19.Stream<_i13.GitReference>.empty(),
- ) as _i19.Stream<_i13.GitReference>);
- @override
- _i19.Future<_i13.GitReference> createReference(
- _i13.RepositorySlug? slug,
- String? ref,
- String? sha,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #createReference,
- [
- slug,
- ref,
- sha,
- ],
- ),
- returnValue: _i19.Future<_i13.GitReference>.value(_FakeGitReference_33(
- this,
- Invocation.method(
- #createReference,
- [
- slug,
- ref,
- sha,
- ],
- ),
- )),
- ) as _i19.Future<_i13.GitReference>);
- @override
- _i19.Future<_i13.GitReference> editReference(
- _i13.RepositorySlug? slug,
- String? ref,
- String? sha, {
- bool? force = false,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #editReference,
- [
- slug,
- ref,
- sha,
- ],
- {#force: force},
- ),
- returnValue: _i19.Future<_i13.GitReference>.value(_FakeGitReference_33(
- this,
- Invocation.method(
- #editReference,
- [
- slug,
- ref,
- sha,
- ],
- {#force: force},
- ),
- )),
- ) as _i19.Future<_i13.GitReference>);
- @override
- _i19.Future<bool> deleteReference(
- _i13.RepositorySlug? slug,
- String? ref,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #deleteReference,
- [
- slug,
- ref,
- ],
- ),
- returnValue: _i19.Future<bool>.value(false),
- ) as _i19.Future<bool>);
- @override
- _i19.Future<_i13.GitTag> getTag(
- _i13.RepositorySlug? slug,
- String? sha,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #getTag,
- [
- slug,
- sha,
- ],
- ),
- returnValue: _i19.Future<_i13.GitTag>.value(_FakeGitTag_37(
- this,
- Invocation.method(
- #getTag,
- [
- slug,
- sha,
- ],
- ),
- )),
- ) as _i19.Future<_i13.GitTag>);
- @override
- _i19.Future<_i13.GitTag> createTag(
- _i13.RepositorySlug? slug,
- _i13.CreateGitTag? tag,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #createTag,
- [
- slug,
- tag,
- ],
- ),
- returnValue: _i19.Future<_i13.GitTag>.value(_FakeGitTag_37(
- this,
- Invocation.method(
- #createTag,
- [
- slug,
- tag,
- ],
- ),
- )),
- ) as _i19.Future<_i13.GitTag>);
- @override
- _i19.Future<_i13.GitTree> getTree(
- _i13.RepositorySlug? slug,
- String? sha, {
- bool? recursive = false,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #getTree,
- [
- slug,
- sha,
- ],
- {#recursive: recursive},
- ),
- returnValue: _i19.Future<_i13.GitTree>.value(_FakeGitTree_38(
- this,
- Invocation.method(
- #getTree,
- [
- slug,
- sha,
- ],
- {#recursive: recursive},
- ),
- )),
- ) as _i19.Future<_i13.GitTree>);
- @override
- _i19.Future<_i13.GitTree> createTree(
- _i13.RepositorySlug? slug,
- _i13.CreateGitTree? tree,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #createTree,
- [
- slug,
- tree,
- ],
- ),
- returnValue: _i19.Future<_i13.GitTree>.value(_FakeGitTree_38(
- this,
- Invocation.method(
- #createTree,
- [
- slug,
- tree,
- ],
- ),
- )),
- ) as _i19.Future<_i13.GitTree>);
-}
-
-/// A class which mocks [GraphQLClient].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockGraphQLClient extends _i1.Mock implements _i14.GraphQLClient {
- MockGraphQLClient() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i14.DefaultPolicies get defaultPolicies => (super.noSuchMethod(
- Invocation.getter(#defaultPolicies),
- returnValue: _FakeDefaultPolicies_39(
- this,
- Invocation.getter(#defaultPolicies),
- ),
- ) as _i14.DefaultPolicies);
- @override
- set defaultPolicies(_i14.DefaultPolicies? _defaultPolicies) => super.noSuchMethod(
- Invocation.setter(
- #defaultPolicies,
- _defaultPolicies,
- ),
- returnValueForMissingStub: null,
- );
- @override
- _i14.Link get link => (super.noSuchMethod(
- Invocation.getter(#link),
- returnValue: _FakeLink_40(
- this,
- Invocation.getter(#link),
- ),
- ) as _i14.Link);
- @override
- _i14.GraphQLCache get cache => (super.noSuchMethod(
- Invocation.getter(#cache),
- returnValue: _FakeGraphQLCache_41(
- this,
- Invocation.getter(#cache),
- ),
- ) as _i14.GraphQLCache);
- @override
- _i14.QueryManager get queryManager => (super.noSuchMethod(
- Invocation.getter(#queryManager),
- returnValue: _FakeQueryManager_42(
- this,
- Invocation.getter(#queryManager),
- ),
- ) as _i14.QueryManager);
- @override
- set queryManager(_i14.QueryManager? _queryManager) => super.noSuchMethod(
- Invocation.setter(
- #queryManager,
- _queryManager,
- ),
- returnValueForMissingStub: null,
- );
- @override
- _i14.GraphQLClient copyWith({
- _i14.Link? link,
- _i14.GraphQLCache? cache,
- _i14.DefaultPolicies? defaultPolicies,
- bool? alwaysRebroadcast,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #copyWith,
- [],
- {
- #link: link,
- #cache: cache,
- #defaultPolicies: defaultPolicies,
- #alwaysRebroadcast: alwaysRebroadcast,
- },
- ),
- returnValue: _FakeGraphQLClient_17(
- this,
- Invocation.method(
- #copyWith,
- [],
- {
- #link: link,
- #cache: cache,
- #defaultPolicies: defaultPolicies,
- #alwaysRebroadcast: alwaysRebroadcast,
- },
- ),
- ),
- ) as _i14.GraphQLClient);
- @override
- _i14.ObservableQuery<TParsed> watchQuery<TParsed>(_i14.WatchQueryOptions<TParsed>? options) => (super.noSuchMethod(
- Invocation.method(
- #watchQuery,
- [options],
- ),
- returnValue: _FakeObservableQuery_43<TParsed>(
- this,
- Invocation.method(
- #watchQuery,
- [options],
- ),
- ),
- ) as _i14.ObservableQuery<TParsed>);
- @override
- _i14.ObservableQuery<TParsed> watchMutation<TParsed>(_i14.WatchQueryOptions<TParsed>? options) => (super.noSuchMethod(
- Invocation.method(
- #watchMutation,
- [options],
- ),
- returnValue: _FakeObservableQuery_43<TParsed>(
- this,
- Invocation.method(
- #watchMutation,
- [options],
- ),
- ),
- ) as _i14.ObservableQuery<TParsed>);
- @override
- _i19.Future<_i14.QueryResult<TParsed>> query<TParsed>(_i14.QueryOptions<TParsed>? options) => (super.noSuchMethod(
- Invocation.method(
- #query,
- [options],
- ),
- returnValue: _i19.Future<_i14.QueryResult<TParsed>>.value(_FakeQueryResult_44<TParsed>(
- this,
- Invocation.method(
- #query,
- [options],
- ),
- )),
- ) as _i19.Future<_i14.QueryResult<TParsed>>);
- @override
- _i19.Future<_i14.QueryResult<TParsed>> mutate<TParsed>(_i14.MutationOptions<TParsed>? options) => (super.noSuchMethod(
- Invocation.method(
- #mutate,
- [options],
- ),
- returnValue: _i19.Future<_i14.QueryResult<TParsed>>.value(_FakeQueryResult_44<TParsed>(
- this,
- Invocation.method(
- #mutate,
- [options],
- ),
- )),
- ) as _i19.Future<_i14.QueryResult<TParsed>>);
- @override
- _i19.Stream<_i14.QueryResult<TParsed>> subscribe<TParsed>(_i14.SubscriptionOptions<TParsed>? options) =>
- (super.noSuchMethod(
- Invocation.method(
- #subscribe,
- [options],
- ),
- returnValue: _i19.Stream<_i14.QueryResult<TParsed>>.empty(),
- ) as _i19.Stream<_i14.QueryResult<TParsed>>);
- @override
- _i19.Future<_i14.QueryResult<TParsed>> fetchMore<TParsed>(
- _i14.FetchMoreOptions? fetchMoreOptions, {
- required _i14.QueryOptions<TParsed>? originalOptions,
- required _i14.QueryResult<TParsed>? previousResult,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #fetchMore,
- [fetchMoreOptions],
- {
- #originalOptions: originalOptions,
- #previousResult: previousResult,
- },
- ),
- returnValue: _i19.Future<_i14.QueryResult<TParsed>>.value(_FakeQueryResult_44<TParsed>(
- this,
- Invocation.method(
- #fetchMore,
- [fetchMoreOptions],
- {
- #originalOptions: originalOptions,
- #previousResult: previousResult,
- },
- ),
- )),
- ) as _i19.Future<_i14.QueryResult<TParsed>>);
- @override
- Map<String, dynamic>? readQuery(
- _i14.Request? request, {
- bool? optimistic = true,
- }) =>
- (super.noSuchMethod(Invocation.method(
- #readQuery,
- [request],
- {#optimistic: optimistic},
- )) as Map<String, dynamic>?);
- @override
- Map<String, dynamic>? readFragment(
- _i14.FragmentRequest? fragmentRequest, {
- bool? optimistic = true,
- }) =>
- (super.noSuchMethod(Invocation.method(
- #readFragment,
- [fragmentRequest],
- {#optimistic: optimistic},
- )) as Map<String, dynamic>?);
- @override
- void writeQuery(
- _i14.Request? request, {
- required Map<String, dynamic>? data,
- bool? broadcast = true,
- }) =>
- super.noSuchMethod(
- Invocation.method(
- #writeQuery,
- [request],
- {
- #data: data,
- #broadcast: broadcast,
- },
- ),
- returnValueForMissingStub: null,
- );
- @override
- void writeFragment(
- _i14.FragmentRequest? fragmentRequest, {
- bool? broadcast = true,
- required Map<String, dynamic>? data,
- }) =>
- super.noSuchMethod(
- Invocation.method(
- #writeFragment,
- [fragmentRequest],
- {
- #broadcast: broadcast,
- #data: data,
- },
- ),
- returnValueForMissingStub: null,
- );
- @override
- _i19.Future<List<_i14.QueryResult<Object?>?>>? resetStore({bool? refetchQueries = true}) =>
- (super.noSuchMethod(Invocation.method(
- #resetStore,
- [],
- {#refetchQueries: refetchQueries},
- )) as _i19.Future<List<_i14.QueryResult<Object?>?>>?);
-}
-
-/// A class which mocks [HttpClient].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockHttpClient extends _i1.Mock implements _i21.HttpClient {
- MockHttpClient() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- Duration get idleTimeout => (super.noSuchMethod(
- Invocation.getter(#idleTimeout),
- returnValue: _FakeDuration_15(
- this,
- Invocation.getter(#idleTimeout),
- ),
- ) as Duration);
- @override
- set idleTimeout(Duration? _idleTimeout) => super.noSuchMethod(
- Invocation.setter(
- #idleTimeout,
- _idleTimeout,
- ),
- returnValueForMissingStub: null,
- );
- @override
- set connectionTimeout(Duration? _connectionTimeout) => super.noSuchMethod(
- Invocation.setter(
- #connectionTimeout,
- _connectionTimeout,
- ),
- returnValueForMissingStub: null,
- );
- @override
- set maxConnectionsPerHost(int? _maxConnectionsPerHost) => super.noSuchMethod(
- Invocation.setter(
- #maxConnectionsPerHost,
- _maxConnectionsPerHost,
- ),
- returnValueForMissingStub: null,
- );
- @override
- bool get autoUncompress => (super.noSuchMethod(
- Invocation.getter(#autoUncompress),
- returnValue: false,
- ) as bool);
- @override
- set autoUncompress(bool? _autoUncompress) => super.noSuchMethod(
- Invocation.setter(
- #autoUncompress,
- _autoUncompress,
- ),
- returnValueForMissingStub: null,
- );
- @override
- set userAgent(String? _userAgent) => super.noSuchMethod(
- Invocation.setter(
- #userAgent,
- _userAgent,
- ),
- returnValueForMissingStub: null,
- );
- @override
- set authenticate(
- _i19.Future<bool> Function(
- Uri,
- String,
- String?,
- )? f) =>
- super.noSuchMethod(
- Invocation.setter(
- #authenticate,
- f,
- ),
- returnValueForMissingStub: null,
- );
- @override
- set connectionFactory(
- _i19.Future<_i21.ConnectionTask<_i21.Socket>> Function(
- Uri,
- String?,
- int?,
- )? f) =>
- super.noSuchMethod(
- Invocation.setter(
- #connectionFactory,
- f,
- ),
- returnValueForMissingStub: null,
- );
- @override
- set findProxy(String Function(Uri)? f) => super.noSuchMethod(
- Invocation.setter(
- #findProxy,
- f,
- ),
- returnValueForMissingStub: null,
- );
- @override
- set authenticateProxy(
- _i19.Future<bool> Function(
- String,
- int,
- String,
- String?,
- )? f) =>
- super.noSuchMethod(
- Invocation.setter(
- #authenticateProxy,
- f,
- ),
- returnValueForMissingStub: null,
- );
- @override
- set badCertificateCallback(
- bool Function(
- _i21.X509Certificate,
- String,
- int,
- )? callback) =>
- super.noSuchMethod(
- Invocation.setter(
- #badCertificateCallback,
- callback,
- ),
- returnValueForMissingStub: null,
- );
- @override
- set keyLog(dynamic Function(String)? callback) => super.noSuchMethod(
- Invocation.setter(
- #keyLog,
- callback,
- ),
- returnValueForMissingStub: null,
- );
- @override
- _i19.Future<_i21.HttpClientRequest> open(
- String? method,
- String? host,
- int? port,
- String? path,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #open,
- [
- method,
- host,
- port,
- path,
- ],
- ),
- returnValue: _i19.Future<_i21.HttpClientRequest>.value(_FakeHttpClientRequest_45(
- this,
- Invocation.method(
- #open,
- [
- method,
- host,
- port,
- path,
- ],
- ),
- )),
- ) as _i19.Future<_i21.HttpClientRequest>);
- @override
- _i19.Future<_i21.HttpClientRequest> openUrl(
- String? method,
- Uri? url,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #openUrl,
- [
- method,
- url,
- ],
- ),
- returnValue: _i19.Future<_i21.HttpClientRequest>.value(_FakeHttpClientRequest_45(
- this,
- Invocation.method(
- #openUrl,
- [
- method,
- url,
- ],
- ),
- )),
- ) as _i19.Future<_i21.HttpClientRequest>);
- @override
- _i19.Future<_i21.HttpClientRequest> get(
- String? host,
- int? port,
- String? path,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #get,
- [
- host,
- port,
- path,
- ],
- ),
- returnValue: _i19.Future<_i21.HttpClientRequest>.value(_FakeHttpClientRequest_45(
- this,
- Invocation.method(
- #get,
- [
- host,
- port,
- path,
- ],
- ),
- )),
- ) as _i19.Future<_i21.HttpClientRequest>);
- @override
- _i19.Future<_i21.HttpClientRequest> getUrl(Uri? url) => (super.noSuchMethod(
- Invocation.method(
- #getUrl,
- [url],
- ),
- returnValue: _i19.Future<_i21.HttpClientRequest>.value(_FakeHttpClientRequest_45(
- this,
- Invocation.method(
- #getUrl,
- [url],
- ),
- )),
- ) as _i19.Future<_i21.HttpClientRequest>);
- @override
- _i19.Future<_i21.HttpClientRequest> post(
- String? host,
- int? port,
- String? path,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #post,
- [
- host,
- port,
- path,
- ],
- ),
- returnValue: _i19.Future<_i21.HttpClientRequest>.value(_FakeHttpClientRequest_45(
- this,
- Invocation.method(
- #post,
- [
- host,
- port,
- path,
- ],
- ),
- )),
- ) as _i19.Future<_i21.HttpClientRequest>);
- @override
- _i19.Future<_i21.HttpClientRequest> postUrl(Uri? url) => (super.noSuchMethod(
- Invocation.method(
- #postUrl,
- [url],
- ),
- returnValue: _i19.Future<_i21.HttpClientRequest>.value(_FakeHttpClientRequest_45(
- this,
- Invocation.method(
- #postUrl,
- [url],
- ),
- )),
- ) as _i19.Future<_i21.HttpClientRequest>);
- @override
- _i19.Future<_i21.HttpClientRequest> put(
- String? host,
- int? port,
- String? path,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #put,
- [
- host,
- port,
- path,
- ],
- ),
- returnValue: _i19.Future<_i21.HttpClientRequest>.value(_FakeHttpClientRequest_45(
- this,
- Invocation.method(
- #put,
- [
- host,
- port,
- path,
- ],
- ),
- )),
- ) as _i19.Future<_i21.HttpClientRequest>);
- @override
- _i19.Future<_i21.HttpClientRequest> putUrl(Uri? url) => (super.noSuchMethod(
- Invocation.method(
- #putUrl,
- [url],
- ),
- returnValue: _i19.Future<_i21.HttpClientRequest>.value(_FakeHttpClientRequest_45(
- this,
- Invocation.method(
- #putUrl,
- [url],
- ),
- )),
- ) as _i19.Future<_i21.HttpClientRequest>);
- @override
- _i19.Future<_i21.HttpClientRequest> delete(
- String? host,
- int? port,
- String? path,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #delete,
- [
- host,
- port,
- path,
- ],
- ),
- returnValue: _i19.Future<_i21.HttpClientRequest>.value(_FakeHttpClientRequest_45(
- this,
- Invocation.method(
- #delete,
- [
- host,
- port,
- path,
- ],
- ),
- )),
- ) as _i19.Future<_i21.HttpClientRequest>);
- @override
- _i19.Future<_i21.HttpClientRequest> deleteUrl(Uri? url) => (super.noSuchMethod(
- Invocation.method(
- #deleteUrl,
- [url],
- ),
- returnValue: _i19.Future<_i21.HttpClientRequest>.value(_FakeHttpClientRequest_45(
- this,
- Invocation.method(
- #deleteUrl,
- [url],
- ),
- )),
- ) as _i19.Future<_i21.HttpClientRequest>);
- @override
- _i19.Future<_i21.HttpClientRequest> patch(
- String? host,
- int? port,
- String? path,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #patch,
- [
- host,
- port,
- path,
- ],
- ),
- returnValue: _i19.Future<_i21.HttpClientRequest>.value(_FakeHttpClientRequest_45(
- this,
- Invocation.method(
- #patch,
- [
- host,
- port,
- path,
- ],
- ),
- )),
- ) as _i19.Future<_i21.HttpClientRequest>);
- @override
- _i19.Future<_i21.HttpClientRequest> patchUrl(Uri? url) => (super.noSuchMethod(
- Invocation.method(
- #patchUrl,
- [url],
- ),
- returnValue: _i19.Future<_i21.HttpClientRequest>.value(_FakeHttpClientRequest_45(
- this,
- Invocation.method(
- #patchUrl,
- [url],
- ),
- )),
- ) as _i19.Future<_i21.HttpClientRequest>);
- @override
- _i19.Future<_i21.HttpClientRequest> head(
- String? host,
- int? port,
- String? path,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #head,
- [
- host,
- port,
- path,
- ],
- ),
- returnValue: _i19.Future<_i21.HttpClientRequest>.value(_FakeHttpClientRequest_45(
- this,
- Invocation.method(
- #head,
- [
- host,
- port,
- path,
- ],
- ),
- )),
- ) as _i19.Future<_i21.HttpClientRequest>);
- @override
- _i19.Future<_i21.HttpClientRequest> headUrl(Uri? url) => (super.noSuchMethod(
- Invocation.method(
- #headUrl,
- [url],
- ),
- returnValue: _i19.Future<_i21.HttpClientRequest>.value(_FakeHttpClientRequest_45(
- this,
- Invocation.method(
- #headUrl,
- [url],
- ),
- )),
- ) as _i19.Future<_i21.HttpClientRequest>);
- @override
- void addCredentials(
- Uri? url,
- String? realm,
- _i21.HttpClientCredentials? credentials,
- ) =>
- super.noSuchMethod(
- Invocation.method(
- #addCredentials,
- [
- url,
- realm,
- credentials,
- ],
- ),
- returnValueForMissingStub: null,
- );
- @override
- void addProxyCredentials(
- String? host,
- int? port,
- String? realm,
- _i21.HttpClientCredentials? credentials,
- ) =>
- super.noSuchMethod(
- Invocation.method(
- #addProxyCredentials,
- [
- host,
- port,
- realm,
- credentials,
- ],
- ),
- returnValueForMissingStub: null,
- );
- @override
- void close({bool? force = false}) => super.noSuchMethod(
- Invocation.method(
- #close,
- [],
- {#force: force},
- ),
- returnValueForMissingStub: null,
- );
-}
-
-/// A class which mocks [HttpClientRequest].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockHttpClientRequest extends _i1.Mock implements _i21.HttpClientRequest {
- MockHttpClientRequest() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- bool get persistentConnection => (super.noSuchMethod(
- Invocation.getter(#persistentConnection),
- returnValue: false,
- ) as bool);
- @override
- set persistentConnection(bool? _persistentConnection) => super.noSuchMethod(
- Invocation.setter(
- #persistentConnection,
- _persistentConnection,
- ),
- returnValueForMissingStub: null,
- );
- @override
- bool get followRedirects => (super.noSuchMethod(
- Invocation.getter(#followRedirects),
- returnValue: false,
- ) as bool);
- @override
- set followRedirects(bool? _followRedirects) => super.noSuchMethod(
- Invocation.setter(
- #followRedirects,
- _followRedirects,
- ),
- returnValueForMissingStub: null,
- );
- @override
- int get maxRedirects => (super.noSuchMethod(
- Invocation.getter(#maxRedirects),
- returnValue: 0,
- ) as int);
- @override
- set maxRedirects(int? _maxRedirects) => super.noSuchMethod(
- Invocation.setter(
- #maxRedirects,
- _maxRedirects,
- ),
- returnValueForMissingStub: null,
- );
- @override
- int get contentLength => (super.noSuchMethod(
- Invocation.getter(#contentLength),
- returnValue: 0,
- ) as int);
- @override
- set contentLength(int? _contentLength) => super.noSuchMethod(
- Invocation.setter(
- #contentLength,
- _contentLength,
- ),
- returnValueForMissingStub: null,
- );
- @override
- bool get bufferOutput => (super.noSuchMethod(
- Invocation.getter(#bufferOutput),
- returnValue: false,
- ) as bool);
- @override
- set bufferOutput(bool? _bufferOutput) => super.noSuchMethod(
- Invocation.setter(
- #bufferOutput,
- _bufferOutput,
- ),
- returnValueForMissingStub: null,
- );
- @override
- String get method => (super.noSuchMethod(
- Invocation.getter(#method),
- returnValue: '',
- ) as String);
- @override
- Uri get uri => (super.noSuchMethod(
- Invocation.getter(#uri),
- returnValue: _FakeUri_46(
- this,
- Invocation.getter(#uri),
- ),
- ) as Uri);
- @override
- _i21.HttpHeaders get headers => (super.noSuchMethod(
- Invocation.getter(#headers),
- returnValue: _FakeHttpHeaders_47(
- this,
- Invocation.getter(#headers),
- ),
- ) as _i21.HttpHeaders);
- @override
- List<_i21.Cookie> get cookies => (super.noSuchMethod(
- Invocation.getter(#cookies),
- returnValue: <_i21.Cookie>[],
- ) as List<_i21.Cookie>);
- @override
- _i19.Future<_i21.HttpClientResponse> get done => (super.noSuchMethod(
- Invocation.getter(#done),
- returnValue: _i19.Future<_i21.HttpClientResponse>.value(_FakeHttpClientResponse_48(
- this,
- Invocation.getter(#done),
- )),
- ) as _i19.Future<_i21.HttpClientResponse>);
- @override
- _i22.Encoding get encoding => (super.noSuchMethod(
- Invocation.getter(#encoding),
- returnValue: _FakeEncoding_49(
- this,
- Invocation.getter(#encoding),
- ),
- ) as _i22.Encoding);
- @override
- set encoding(_i22.Encoding? _encoding) => super.noSuchMethod(
- Invocation.setter(
- #encoding,
- _encoding,
- ),
- returnValueForMissingStub: null,
- );
- @override
- _i19.Future<_i21.HttpClientResponse> close() => (super.noSuchMethod(
- Invocation.method(
- #close,
- [],
- ),
- returnValue: _i19.Future<_i21.HttpClientResponse>.value(_FakeHttpClientResponse_48(
- this,
- Invocation.method(
- #close,
- [],
- ),
- )),
- ) as _i19.Future<_i21.HttpClientResponse>);
- @override
- void abort([
- Object? exception,
- StackTrace? stackTrace,
- ]) =>
- super.noSuchMethod(
- Invocation.method(
- #abort,
- [
- exception,
- stackTrace,
- ],
- ),
- returnValueForMissingStub: null,
- );
- @override
- void add(List<int>? data) => super.noSuchMethod(
- Invocation.method(
- #add,
- [data],
- ),
- returnValueForMissingStub: null,
- );
- @override
- void write(Object? object) => super.noSuchMethod(
- Invocation.method(
- #write,
- [object],
- ),
- returnValueForMissingStub: null,
- );
- @override
- void writeAll(
- Iterable<dynamic>? objects, [
- String? separator = r'',
- ]) =>
- super.noSuchMethod(
- Invocation.method(
- #writeAll,
- [
- objects,
- separator,
- ],
- ),
- returnValueForMissingStub: null,
- );
- @override
- void writeln([Object? object = r'']) => super.noSuchMethod(
- Invocation.method(
- #writeln,
- [object],
- ),
- returnValueForMissingStub: null,
- );
- @override
- void writeCharCode(int? charCode) => super.noSuchMethod(
- Invocation.method(
- #writeCharCode,
- [charCode],
- ),
- returnValueForMissingStub: null,
- );
- @override
- void addError(
- Object? error, [
- StackTrace? stackTrace,
- ]) =>
- super.noSuchMethod(
- Invocation.method(
- #addError,
- [
- error,
- stackTrace,
- ],
- ),
- returnValueForMissingStub: null,
- );
- @override
- _i19.Future<dynamic> addStream(_i19.Stream<List<int>>? stream) => (super.noSuchMethod(
- Invocation.method(
- #addStream,
- [stream],
- ),
- returnValue: _i19.Future<dynamic>.value(),
- ) as _i19.Future<dynamic>);
- @override
- _i19.Future<dynamic> flush() => (super.noSuchMethod(
- Invocation.method(
- #flush,
- [],
- ),
- returnValue: _i19.Future<dynamic>.value(),
- ) as _i19.Future<dynamic>);
-}
-
-/// A class which mocks [HttpClientResponse].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockHttpClientResponse extends _i1.Mock implements _i21.HttpClientResponse {
- MockHttpClientResponse() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- int get statusCode => (super.noSuchMethod(
- Invocation.getter(#statusCode),
- returnValue: 0,
- ) as int);
- @override
- String get reasonPhrase => (super.noSuchMethod(
- Invocation.getter(#reasonPhrase),
- returnValue: '',
- ) as String);
- @override
- int get contentLength => (super.noSuchMethod(
- Invocation.getter(#contentLength),
- returnValue: 0,
- ) as int);
- @override
- _i21.HttpClientResponseCompressionState get compressionState => (super.noSuchMethod(
- Invocation.getter(#compressionState),
- returnValue: _i21.HttpClientResponseCompressionState.notCompressed,
- ) as _i21.HttpClientResponseCompressionState);
- @override
- bool get persistentConnection => (super.noSuchMethod(
- Invocation.getter(#persistentConnection),
- returnValue: false,
- ) as bool);
- @override
- bool get isRedirect => (super.noSuchMethod(
- Invocation.getter(#isRedirect),
- returnValue: false,
- ) as bool);
- @override
- List<_i21.RedirectInfo> get redirects => (super.noSuchMethod(
- Invocation.getter(#redirects),
- returnValue: <_i21.RedirectInfo>[],
- ) as List<_i21.RedirectInfo>);
- @override
- _i21.HttpHeaders get headers => (super.noSuchMethod(
- Invocation.getter(#headers),
- returnValue: _FakeHttpHeaders_47(
- this,
- Invocation.getter(#headers),
- ),
- ) as _i21.HttpHeaders);
- @override
- List<_i21.Cookie> get cookies => (super.noSuchMethod(
- Invocation.getter(#cookies),
- returnValue: <_i21.Cookie>[],
- ) as List<_i21.Cookie>);
- @override
- bool get isBroadcast => (super.noSuchMethod(
- Invocation.getter(#isBroadcast),
- returnValue: false,
- ) as bool);
- @override
- _i19.Future<int> get length => (super.noSuchMethod(
- Invocation.getter(#length),
- returnValue: _i19.Future<int>.value(0),
- ) as _i19.Future<int>);
- @override
- _i19.Future<bool> get isEmpty => (super.noSuchMethod(
- Invocation.getter(#isEmpty),
- returnValue: _i19.Future<bool>.value(false),
- ) as _i19.Future<bool>);
- @override
- _i19.Future<List<int>> get first => (super.noSuchMethod(
- Invocation.getter(#first),
- returnValue: _i19.Future<List<int>>.value(<int>[]),
- ) as _i19.Future<List<int>>);
- @override
- _i19.Future<List<int>> get last => (super.noSuchMethod(
- Invocation.getter(#last),
- returnValue: _i19.Future<List<int>>.value(<int>[]),
- ) as _i19.Future<List<int>>);
- @override
- _i19.Future<List<int>> get single => (super.noSuchMethod(
- Invocation.getter(#single),
- returnValue: _i19.Future<List<int>>.value(<int>[]),
- ) as _i19.Future<List<int>>);
- @override
- _i19.Future<_i21.HttpClientResponse> redirect([
- String? method,
- Uri? url,
- bool? followLoops,
- ]) =>
- (super.noSuchMethod(
- Invocation.method(
- #redirect,
- [
- method,
- url,
- followLoops,
- ],
- ),
- returnValue: _i19.Future<_i21.HttpClientResponse>.value(_FakeHttpClientResponse_48(
- this,
- Invocation.method(
- #redirect,
- [
- method,
- url,
- followLoops,
- ],
- ),
- )),
- ) as _i19.Future<_i21.HttpClientResponse>);
- @override
- _i19.Future<_i21.Socket> detachSocket() => (super.noSuchMethod(
- Invocation.method(
- #detachSocket,
- [],
- ),
- returnValue: _i19.Future<_i21.Socket>.value(_FakeSocket_50(
- this,
- Invocation.method(
- #detachSocket,
- [],
- ),
- )),
- ) as _i19.Future<_i21.Socket>);
- @override
- _i19.Stream<List<int>> asBroadcastStream({
- void Function(_i19.StreamSubscription<List<int>>)? onListen,
- void Function(_i19.StreamSubscription<List<int>>)? onCancel,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #asBroadcastStream,
- [],
- {
- #onListen: onListen,
- #onCancel: onCancel,
- },
- ),
- returnValue: _i19.Stream<List<int>>.empty(),
- ) as _i19.Stream<List<int>>);
- @override
- _i19.StreamSubscription<List<int>> listen(
- void Function(List<int>)? onData, {
- Function? onError,
- void Function()? onDone,
- bool? cancelOnError,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #listen,
- [onData],
- {
- #onError: onError,
- #onDone: onDone,
- #cancelOnError: cancelOnError,
- },
- ),
- returnValue: _FakeStreamSubscription_51<List<int>>(
- this,
- Invocation.method(
- #listen,
- [onData],
- {
- #onError: onError,
- #onDone: onDone,
- #cancelOnError: cancelOnError,
- },
- ),
- ),
- ) as _i19.StreamSubscription<List<int>>);
- @override
- _i19.Stream<List<int>> where(bool Function(List<int>)? test) => (super.noSuchMethod(
- Invocation.method(
- #where,
- [test],
- ),
- returnValue: _i19.Stream<List<int>>.empty(),
- ) as _i19.Stream<List<int>>);
- @override
- _i19.Stream<S> map<S>(S Function(List<int>)? convert) => (super.noSuchMethod(
- Invocation.method(
- #map,
- [convert],
- ),
- returnValue: _i19.Stream<S>.empty(),
- ) as _i19.Stream<S>);
- @override
- _i19.Stream<E> asyncMap<E>(_i19.FutureOr<E> Function(List<int>)? convert) => (super.noSuchMethod(
- Invocation.method(
- #asyncMap,
- [convert],
- ),
- returnValue: _i19.Stream<E>.empty(),
- ) as _i19.Stream<E>);
- @override
- _i19.Stream<E> asyncExpand<E>(_i19.Stream<E>? Function(List<int>)? convert) => (super.noSuchMethod(
- Invocation.method(
- #asyncExpand,
- [convert],
- ),
- returnValue: _i19.Stream<E>.empty(),
- ) as _i19.Stream<E>);
- @override
- _i19.Stream<List<int>> handleError(
- Function? onError, {
- bool Function(dynamic)? test,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #handleError,
- [onError],
- {#test: test},
- ),
- returnValue: _i19.Stream<List<int>>.empty(),
- ) as _i19.Stream<List<int>>);
- @override
- _i19.Stream<S> expand<S>(Iterable<S> Function(List<int>)? convert) => (super.noSuchMethod(
- Invocation.method(
- #expand,
- [convert],
- ),
- returnValue: _i19.Stream<S>.empty(),
- ) as _i19.Stream<S>);
- @override
- _i19.Future<dynamic> pipe(_i19.StreamConsumer<List<int>>? streamConsumer) => (super.noSuchMethod(
- Invocation.method(
- #pipe,
- [streamConsumer],
- ),
- returnValue: _i19.Future<dynamic>.value(),
- ) as _i19.Future<dynamic>);
- @override
- _i19.Stream<S> transform<S>(_i19.StreamTransformer<List<int>, S>? streamTransformer) => (super.noSuchMethod(
- Invocation.method(
- #transform,
- [streamTransformer],
- ),
- returnValue: _i19.Stream<S>.empty(),
- ) as _i19.Stream<S>);
- @override
- _i19.Future<List<int>> reduce(
- List<int> Function(
- List<int>,
- List<int>,
- )? combine) =>
- (super.noSuchMethod(
- Invocation.method(
- #reduce,
- [combine],
- ),
- returnValue: _i19.Future<List<int>>.value(<int>[]),
- ) as _i19.Future<List<int>>);
- @override
- _i19.Future<S> fold<S>(
- S? initialValue,
- S Function(
- S,
- List<int>,
- )? combine,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #fold,
- [
- initialValue,
- combine,
- ],
- ),
- returnValue: _i27.ifNotNull(
- _i27.dummyValueOrNull<S>(
- this,
- Invocation.method(
- #fold,
- [
- initialValue,
- combine,
- ],
- ),
- ),
- (S v) => _i19.Future<S>.value(v),
- ) ??
- _FakeFuture_22<S>(
- this,
- Invocation.method(
- #fold,
- [
- initialValue,
- combine,
- ],
- ),
- ),
- ) as _i19.Future<S>);
- @override
- _i19.Future<String> join([String? separator = r'']) => (super.noSuchMethod(
- Invocation.method(
- #join,
- [separator],
- ),
- returnValue: _i19.Future<String>.value(''),
- ) as _i19.Future<String>);
- @override
- _i19.Future<bool> contains(Object? needle) => (super.noSuchMethod(
- Invocation.method(
- #contains,
- [needle],
- ),
- returnValue: _i19.Future<bool>.value(false),
- ) as _i19.Future<bool>);
- @override
- _i19.Future<void> forEach(void Function(List<int>)? action) => (super.noSuchMethod(
- Invocation.method(
- #forEach,
- [action],
- ),
- returnValue: _i19.Future<void>.value(),
- returnValueForMissingStub: _i19.Future<void>.value(),
- ) as _i19.Future<void>);
- @override
- _i19.Future<bool> every(bool Function(List<int>)? test) => (super.noSuchMethod(
- Invocation.method(
- #every,
- [test],
- ),
- returnValue: _i19.Future<bool>.value(false),
- ) as _i19.Future<bool>);
- @override
- _i19.Future<bool> any(bool Function(List<int>)? test) => (super.noSuchMethod(
- Invocation.method(
- #any,
- [test],
- ),
- returnValue: _i19.Future<bool>.value(false),
- ) as _i19.Future<bool>);
- @override
- _i19.Stream<R> cast<R>() => (super.noSuchMethod(
- Invocation.method(
- #cast,
- [],
- ),
- returnValue: _i19.Stream<R>.empty(),
- ) as _i19.Stream<R>);
- @override
- _i19.Future<List<List<int>>> toList() => (super.noSuchMethod(
- Invocation.method(
- #toList,
- [],
- ),
- returnValue: _i19.Future<List<List<int>>>.value(<List<int>>[]),
- ) as _i19.Future<List<List<int>>>);
- @override
- _i19.Future<Set<List<int>>> toSet() => (super.noSuchMethod(
- Invocation.method(
- #toSet,
- [],
- ),
- returnValue: _i19.Future<Set<List<int>>>.value(<List<int>>{}),
- ) as _i19.Future<Set<List<int>>>);
- @override
- _i19.Future<E> drain<E>([E? futureValue]) => (super.noSuchMethod(
- Invocation.method(
- #drain,
- [futureValue],
- ),
- returnValue: _i27.ifNotNull(
- _i27.dummyValueOrNull<E>(
- this,
- Invocation.method(
- #drain,
- [futureValue],
- ),
- ),
- (E v) => _i19.Future<E>.value(v),
- ) ??
- _FakeFuture_22<E>(
- this,
- Invocation.method(
- #drain,
- [futureValue],
- ),
- ),
- ) as _i19.Future<E>);
- @override
- _i19.Stream<List<int>> take(int? count) => (super.noSuchMethod(
- Invocation.method(
- #take,
- [count],
- ),
- returnValue: _i19.Stream<List<int>>.empty(),
- ) as _i19.Stream<List<int>>);
- @override
- _i19.Stream<List<int>> takeWhile(bool Function(List<int>)? test) => (super.noSuchMethod(
- Invocation.method(
- #takeWhile,
- [test],
- ),
- returnValue: _i19.Stream<List<int>>.empty(),
- ) as _i19.Stream<List<int>>);
- @override
- _i19.Stream<List<int>> skip(int? count) => (super.noSuchMethod(
- Invocation.method(
- #skip,
- [count],
- ),
- returnValue: _i19.Stream<List<int>>.empty(),
- ) as _i19.Stream<List<int>>);
- @override
- _i19.Stream<List<int>> skipWhile(bool Function(List<int>)? test) => (super.noSuchMethod(
- Invocation.method(
- #skipWhile,
- [test],
- ),
- returnValue: _i19.Stream<List<int>>.empty(),
- ) as _i19.Stream<List<int>>);
- @override
- _i19.Stream<List<int>> distinct(
- [bool Function(
- List<int>,
- List<int>,
- )? equals]) =>
- (super.noSuchMethod(
- Invocation.method(
- #distinct,
- [equals],
- ),
- returnValue: _i19.Stream<List<int>>.empty(),
- ) as _i19.Stream<List<int>>);
- @override
- _i19.Future<List<int>> firstWhere(
- bool Function(List<int>)? test, {
- List<int> Function()? orElse,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #firstWhere,
- [test],
- {#orElse: orElse},
- ),
- returnValue: _i19.Future<List<int>>.value(<int>[]),
- ) as _i19.Future<List<int>>);
- @override
- _i19.Future<List<int>> lastWhere(
- bool Function(List<int>)? test, {
- List<int> Function()? orElse,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #lastWhere,
- [test],
- {#orElse: orElse},
- ),
- returnValue: _i19.Future<List<int>>.value(<int>[]),
- ) as _i19.Future<List<int>>);
- @override
- _i19.Future<List<int>> singleWhere(
- bool Function(List<int>)? test, {
- List<int> Function()? orElse,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #singleWhere,
- [test],
- {#orElse: orElse},
- ),
- returnValue: _i19.Future<List<int>>.value(<int>[]),
- ) as _i19.Future<List<int>>);
- @override
- _i19.Future<List<int>> elementAt(int? index) => (super.noSuchMethod(
- Invocation.method(
- #elementAt,
- [index],
- ),
- returnValue: _i19.Future<List<int>>.value(<int>[]),
- ) as _i19.Future<List<int>>);
- @override
- _i19.Stream<List<int>> timeout(
- Duration? timeLimit, {
- void Function(_i19.EventSink<List<int>>)? onTimeout,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #timeout,
- [timeLimit],
- {#onTimeout: onTimeout},
- ),
- returnValue: _i19.Stream<List<int>>.empty(),
- ) as _i19.Stream<List<int>>);
-}
-
-/// A class which mocks [JobsResource].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockJobsResource extends _i1.Mock implements _i6.JobsResource {
- MockJobsResource() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i19.Future<_i6.JobCancelResponse> cancel(
- String? projectId,
- String? jobId, {
- String? location,
- String? $fields,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #cancel,
- [
- projectId,
- jobId,
- ],
- {
- #location: location,
- #$fields: $fields,
- },
- ),
- returnValue: _i19.Future<_i6.JobCancelResponse>.value(_FakeJobCancelResponse_52(
- this,
- Invocation.method(
- #cancel,
- [
- projectId,
- jobId,
- ],
- {
- #location: location,
- #$fields: $fields,
- },
- ),
- )),
- ) as _i19.Future<_i6.JobCancelResponse>);
- @override
- _i19.Future<void> delete(
- String? projectId,
- String? jobId, {
- String? location,
- String? $fields,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #delete,
- [
- projectId,
- jobId,
- ],
- {
- #location: location,
- #$fields: $fields,
- },
- ),
- returnValue: _i19.Future<void>.value(),
- returnValueForMissingStub: _i19.Future<void>.value(),
- ) as _i19.Future<void>);
- @override
- _i19.Future<_i6.Job> get(
- String? projectId,
- String? jobId, {
- String? location,
- String? $fields,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #get,
- [
- projectId,
- jobId,
- ],
- {
- #location: location,
- #$fields: $fields,
- },
- ),
- returnValue: _i19.Future<_i6.Job>.value(_FakeJob_53(
- this,
- Invocation.method(
- #get,
- [
- projectId,
- jobId,
- ],
- {
- #location: location,
- #$fields: $fields,
- },
- ),
- )),
- ) as _i19.Future<_i6.Job>);
- @override
- _i19.Future<_i6.GetQueryResultsResponse> getQueryResults(
- String? projectId,
- String? jobId, {
- String? location,
- int? maxResults,
- String? pageToken,
- String? startIndex,
- int? timeoutMs,
- String? $fields,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #getQueryResults,
- [
- projectId,
- jobId,
- ],
- {
- #location: location,
- #maxResults: maxResults,
- #pageToken: pageToken,
- #startIndex: startIndex,
- #timeoutMs: timeoutMs,
- #$fields: $fields,
- },
- ),
- returnValue: _i19.Future<_i6.GetQueryResultsResponse>.value(_FakeGetQueryResultsResponse_54(
- this,
- Invocation.method(
- #getQueryResults,
- [
- projectId,
- jobId,
- ],
- {
- #location: location,
- #maxResults: maxResults,
- #pageToken: pageToken,
- #startIndex: startIndex,
- #timeoutMs: timeoutMs,
- #$fields: $fields,
- },
- ),
- )),
- ) as _i19.Future<_i6.GetQueryResultsResponse>);
- @override
- _i19.Future<_i6.Job> insert(
- _i6.Job? request,
- String? projectId, {
- String? $fields,
- _i6.UploadOptions? uploadOptions = _i6.UploadOptions.defaultOptions,
- _i6.Media? uploadMedia,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #insert,
- [
- request,
- projectId,
- ],
- {
- #$fields: $fields,
- #uploadOptions: uploadOptions,
- #uploadMedia: uploadMedia,
- },
- ),
- returnValue: _i19.Future<_i6.Job>.value(_FakeJob_53(
- this,
- Invocation.method(
- #insert,
- [
- request,
- projectId,
- ],
- {
- #$fields: $fields,
- #uploadOptions: uploadOptions,
- #uploadMedia: uploadMedia,
- },
- ),
- )),
- ) as _i19.Future<_i6.Job>);
- @override
- _i19.Future<_i6.JobList> list(
- String? projectId, {
- bool? allUsers,
- String? maxCreationTime,
- int? maxResults,
- String? minCreationTime,
- String? pageToken,
- String? parentJobId,
- String? projection,
- List<String>? stateFilter,
- String? $fields,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #list,
- [projectId],
- {
- #allUsers: allUsers,
- #maxCreationTime: maxCreationTime,
- #maxResults: maxResults,
- #minCreationTime: minCreationTime,
- #pageToken: pageToken,
- #parentJobId: parentJobId,
- #projection: projection,
- #stateFilter: stateFilter,
- #$fields: $fields,
- },
- ),
- returnValue: _i19.Future<_i6.JobList>.value(_FakeJobList_55(
- this,
- Invocation.method(
- #list,
- [projectId],
- {
- #allUsers: allUsers,
- #maxCreationTime: maxCreationTime,
- #maxResults: maxResults,
- #minCreationTime: minCreationTime,
- #pageToken: pageToken,
- #parentJobId: parentJobId,
- #projection: projection,
- #stateFilter: stateFilter,
- #$fields: $fields,
- },
- ),
- )),
- ) as _i19.Future<_i6.JobList>);
- @override
- _i19.Future<_i6.QueryResponse> query(
- _i6.QueryRequest? request,
- String? projectId, {
- String? $fields,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #query,
- [
- request,
- projectId,
- ],
- {#$fields: $fields},
- ),
- returnValue: _i19.Future<_i6.QueryResponse>.value(_FakeQueryResponse_56(
- this,
- Invocation.method(
- #query,
- [
- request,
- projectId,
- ],
- {#$fields: $fields},
- ),
- )),
- ) as _i19.Future<_i6.QueryResponse>);
-}
-
-/// A class which mocks [LuciBuildService].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockLuciBuildService extends _i1.Mock implements _i23.LuciBuildService {
- MockLuciBuildService() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i23.BuildBucketClient get buildBucketClient => (super.noSuchMethod(
- Invocation.getter(#buildBucketClient),
- returnValue: _FakeBuildBucketClient_57(
- this,
- Invocation.getter(#buildBucketClient),
- ),
- ) as _i23.BuildBucketClient);
- @override
- set buildBucketClient(_i23.BuildBucketClient? _buildBucketClient) => super.noSuchMethod(
- Invocation.setter(
- #buildBucketClient,
- _buildBucketClient,
- ),
- returnValueForMissingStub: null,
- );
- @override
- _i23.CacheService get cache => (super.noSuchMethod(
- Invocation.getter(#cache),
- returnValue: _FakeCacheService_58(
- this,
- Invocation.getter(#cache),
- ),
- ) as _i23.CacheService);
- @override
- _i3.Config get config => (super.noSuchMethod(
- Invocation.getter(#config),
- returnValue: _FakeConfig_1(
- this,
- Invocation.getter(#config),
- ),
- ) as _i3.Config);
- @override
- set config(_i3.Config? _config) => super.noSuchMethod(
- Invocation.setter(
- #config,
- _config,
- ),
- returnValueForMissingStub: null,
- );
- @override
- _i20.GithubChecksUtil get githubChecksUtil => (super.noSuchMethod(
- Invocation.getter(#githubChecksUtil),
- returnValue: _FakeGithubChecksUtil_27(
- this,
- Invocation.getter(#githubChecksUtil),
- ),
- ) as _i20.GithubChecksUtil);
- @override
- set githubChecksUtil(_i20.GithubChecksUtil? _githubChecksUtil) => super.noSuchMethod(
- Invocation.setter(
- #githubChecksUtil,
- _githubChecksUtil,
- ),
- returnValueForMissingStub: null,
- );
- @override
- _i7.GerritService get gerritService => (super.noSuchMethod(
- Invocation.getter(#gerritService),
- returnValue: _FakeGerritService_6(
- this,
- Invocation.getter(#gerritService),
- ),
- ) as _i7.GerritService);
- @override
- set gerritService(_i7.GerritService? _gerritService) => super.noSuchMethod(
- Invocation.setter(
- #gerritService,
- _gerritService,
- ),
- returnValueForMissingStub: null,
- );
- @override
- _i23.PubSub get pubsub => (super.noSuchMethod(
- Invocation.getter(#pubsub),
- returnValue: _FakePubSub_59(
- this,
- Invocation.getter(#pubsub),
- ),
- ) as _i23.PubSub);
- @override
- _i19.Future<List<List<_i8.Request>>> shard(
- List<_i8.Request>? requests,
- int? max,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #shard,
- [
- requests,
- max,
- ],
- ),
- returnValue: _i19.Future<List<List<_i8.Request>>>.value(<List<_i8.Request>>[]),
- ) as _i19.Future<List<List<_i8.Request>>>);
- @override
- _i19.Future<Iterable<_i8.Build>> getTryBuilds(
- _i13.RepositorySlug? slug,
- String? sha,
- String? builderName,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #getTryBuilds,
- [
- slug,
- sha,
- builderName,
- ],
- ),
- returnValue: _i19.Future<Iterable<_i8.Build>>.value(<_i8.Build>[]),
- ) as _i19.Future<Iterable<_i8.Build>>);
- @override
- _i19.Future<Iterable<_i8.Build>> getTryBuildsByPullRequest(_i13.PullRequest? pullRequest) => (super.noSuchMethod(
- Invocation.method(
- #getTryBuildsByPullRequest,
- [pullRequest],
- ),
- returnValue: _i19.Future<Iterable<_i8.Build>>.value(<_i8.Build>[]),
- ) as _i19.Future<Iterable<_i8.Build>>);
- @override
- _i19.Future<Iterable<_i8.Build>> getProdBuilds(
- _i13.RepositorySlug? slug,
- String? commitSha,
- String? builderName,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #getProdBuilds,
- [
- slug,
- commitSha,
- builderName,
- ],
- ),
- returnValue: _i19.Future<Iterable<_i8.Build>>.value(<_i8.Build>[]),
- ) as _i19.Future<Iterable<_i8.Build>>);
- @override
- _i19.Future<Iterable<_i8.Build>> getBuilds(
- _i13.RepositorySlug? slug,
- String? commitSha,
- String? builderName,
- String? bucket,
- Map<String, List<String>>? tags,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #getBuilds,
- [
- slug,
- commitSha,
- builderName,
- bucket,
- tags,
- ],
- ),
- returnValue: _i19.Future<Iterable<_i8.Build>>.value(<_i8.Build>[]),
- ) as _i19.Future<Iterable<_i8.Build>>);
- @override
- _i19.Future<List<_i37.Target>> scheduleTryBuilds({
- required List<_i37.Target>? targets,
- required _i13.PullRequest? pullRequest,
- _i29.CheckSuiteEvent? checkSuiteEvent,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #scheduleTryBuilds,
- [],
- {
- #targets: targets,
- #pullRequest: pullRequest,
- #checkSuiteEvent: checkSuiteEvent,
- },
- ),
- returnValue: _i19.Future<List<_i37.Target>>.value(<_i37.Target>[]),
- ) as _i19.Future<List<_i37.Target>>);
- @override
- _i19.Future<void> cancelBuilds(
- _i13.PullRequest? pullRequest,
- String? reason,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #cancelBuilds,
- [
- pullRequest,
- reason,
- ],
- ),
- returnValue: _i19.Future<void>.value(),
- returnValueForMissingStub: _i19.Future<void>.value(),
- ) as _i19.Future<void>);
- @override
- _i19.Future<List<_i8.Build?>> failedBuilds(
- _i13.PullRequest? pullRequest,
- List<_i37.Target>? targets,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #failedBuilds,
- [
- pullRequest,
- targets,
- ],
- ),
- returnValue: _i19.Future<List<_i8.Build?>>.value(<_i8.Build?>[]),
- ) as _i19.Future<List<_i8.Build?>>);
- @override
- _i19.Future<_i8.Build> rescheduleBuild({
- required String? builderName,
- required _i36.BuildPushMessage? buildPushMessage,
- required int? rescheduleAttempt,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #rescheduleBuild,
- [],
- {
- #builderName: builderName,
- #buildPushMessage: buildPushMessage,
- #rescheduleAttempt: rescheduleAttempt,
- },
- ),
- returnValue: _i19.Future<_i8.Build>.value(_FakeBuild_7(
- this,
- Invocation.method(
- #rescheduleBuild,
- [],
- {
- #builderName: builderName,
- #buildPushMessage: buildPushMessage,
- #rescheduleAttempt: rescheduleAttempt,
- },
- ),
- )),
- ) as _i19.Future<_i8.Build>);
- @override
- _i19.Future<_i8.Build> reschedulePresubmitBuildUsingCheckRunEvent(_i38.CheckRunEvent? checkRunEvent) =>
- (super.noSuchMethod(
- Invocation.method(
- #reschedulePresubmitBuildUsingCheckRunEvent,
- [checkRunEvent],
- ),
- returnValue: _i19.Future<_i8.Build>.value(_FakeBuild_7(
- this,
- Invocation.method(
- #reschedulePresubmitBuildUsingCheckRunEvent,
- [checkRunEvent],
- ),
- )),
- ) as _i19.Future<_i8.Build>);
- @override
- _i19.Future<_i8.Build> reschedulePostsubmitBuildUsingCheckRunEvent(
- _i38.CheckRunEvent? checkRunEvent, {
- required _i31.Commit? commit,
- required _i32.Task? task,
- required _i37.Target? target,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #reschedulePostsubmitBuildUsingCheckRunEvent,
- [checkRunEvent],
- {
- #commit: commit,
- #task: task,
- #target: target,
- },
- ),
- returnValue: _i19.Future<_i8.Build>.value(_FakeBuild_7(
- this,
- Invocation.method(
- #reschedulePostsubmitBuildUsingCheckRunEvent,
- [checkRunEvent],
- {
- #commit: commit,
- #task: task,
- #target: target,
- },
- ),
- )),
- ) as _i19.Future<_i8.Build>);
- @override
- _i19.Future<_i8.Build> getBuildById(
- String? id, {
- String? fields,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #getBuildById,
- [id],
- {#fields: fields},
- ),
- returnValue: _i19.Future<_i8.Build>.value(_FakeBuild_7(
- this,
- Invocation.method(
- #getBuildById,
- [id],
- {#fields: fields},
- ),
- )),
- ) as _i19.Future<_i8.Build>);
- @override
- _i19.Future<Set<String>> getAvailableBuilderSet({
- String? project = r'flutter',
- String? bucket = r'prod',
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #getAvailableBuilderSet,
- [],
- {
- #project: project,
- #bucket: bucket,
- },
- ),
- returnValue: _i19.Future<Set<String>>.value(<String>{}),
- ) as _i19.Future<Set<String>>);
- @override
- _i19.Future<List<_i23.Tuple<_i37.Target, _i32.Task, int>>> schedulePostsubmitBuilds({
- required _i31.Commit? commit,
- required List<_i23.Tuple<_i37.Target, _i32.Task, int>>? toBeScheduled,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #schedulePostsubmitBuilds,
- [],
- {
- #commit: commit,
- #toBeScheduled: toBeScheduled,
- },
- ),
- returnValue: _i19.Future<List<_i23.Tuple<_i37.Target, _i32.Task, int>>>.value(
- <_i23.Tuple<_i37.Target, _i32.Task, int>>[]),
- ) as _i19.Future<List<_i23.Tuple<_i37.Target, _i32.Task, int>>>);
- @override
- _i19.Future<void> createPostsubmitCheckRun(
- _i31.Commit? commit,
- _i37.Target? target,
- Map<String, dynamic>? rawUserData,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #createPostsubmitCheckRun,
- [
- commit,
- target,
- rawUserData,
- ],
- ),
- returnValue: _i19.Future<void>.value(),
- returnValueForMissingStub: _i19.Future<void>.value(),
- ) as _i19.Future<void>);
- @override
- _i19.Future<bool> checkRerunBuilder({
- required _i31.Commit? commit,
- required _i37.Target? target,
- required _i32.Task? task,
- required _i9.DatastoreService? datastore,
- Map<String, List<String>>? tags,
- bool? ignoreChecks = false,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #checkRerunBuilder,
- [],
- {
- #commit: commit,
- #target: target,
- #task: task,
- #datastore: datastore,
- #tags: tags,
- #ignoreChecks: ignoreChecks,
- },
- ),
- returnValue: _i19.Future<bool>.value(false),
- ) as _i19.Future<bool>);
-}
-
-/// A class which mocks [ProcessManager].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockProcessManager extends _i1.Mock implements _i39.ProcessManager {
- MockProcessManager() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i19.Future<_i21.Process> start(
- List<Object>? command, {
- String? workingDirectory,
- Map<String, String>? environment,
- bool? includeParentEnvironment = true,
- bool? runInShell = false,
- _i21.ProcessStartMode? mode = _i21.ProcessStartMode.normal,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #start,
- [command],
- {
- #workingDirectory: workingDirectory,
- #environment: environment,
- #includeParentEnvironment: includeParentEnvironment,
- #runInShell: runInShell,
- #mode: mode,
- },
- ),
- returnValue: _i19.Future<_i21.Process>.value(_FakeProcess_60(
- this,
- Invocation.method(
- #start,
- [command],
- {
- #workingDirectory: workingDirectory,
- #environment: environment,
- #includeParentEnvironment: includeParentEnvironment,
- #runInShell: runInShell,
- #mode: mode,
- },
- ),
- )),
- ) as _i19.Future<_i21.Process>);
- @override
- _i19.Future<_i21.ProcessResult> run(
- List<Object>? command, {
- String? workingDirectory,
- Map<String, String>? environment,
- bool? includeParentEnvironment = true,
- bool? runInShell = false,
- _i22.Encoding? stdoutEncoding = const _i21.SystemEncoding(),
- _i22.Encoding? stderrEncoding = const _i21.SystemEncoding(),
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #run,
- [command],
- {
- #workingDirectory: workingDirectory,
- #environment: environment,
- #includeParentEnvironment: includeParentEnvironment,
- #runInShell: runInShell,
- #stdoutEncoding: stdoutEncoding,
- #stderrEncoding: stderrEncoding,
- },
- ),
- returnValue: _i19.Future<_i21.ProcessResult>.value(_i27.dummyValue<_i21.ProcessResult>(
- this,
- Invocation.method(
- #run,
- [command],
- {
- #workingDirectory: workingDirectory,
- #environment: environment,
- #includeParentEnvironment: includeParentEnvironment,
- #runInShell: runInShell,
- #stdoutEncoding: stdoutEncoding,
- #stderrEncoding: stderrEncoding,
- },
- ),
- )),
- ) as _i19.Future<_i21.ProcessResult>);
- @override
- _i21.ProcessResult runSync(
- List<Object>? command, {
- String? workingDirectory,
- Map<String, String>? environment,
- bool? includeParentEnvironment = true,
- bool? runInShell = false,
- _i22.Encoding? stdoutEncoding = const _i21.SystemEncoding(),
- _i22.Encoding? stderrEncoding = const _i21.SystemEncoding(),
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #runSync,
- [command],
- {
- #workingDirectory: workingDirectory,
- #environment: environment,
- #includeParentEnvironment: includeParentEnvironment,
- #runInShell: runInShell,
- #stdoutEncoding: stdoutEncoding,
- #stderrEncoding: stderrEncoding,
- },
- ),
- returnValue: _i27.dummyValue<_i21.ProcessResult>(
- this,
- Invocation.method(
- #runSync,
- [command],
- {
- #workingDirectory: workingDirectory,
- #environment: environment,
- #includeParentEnvironment: includeParentEnvironment,
- #runInShell: runInShell,
- #stdoutEncoding: stdoutEncoding,
- #stderrEncoding: stderrEncoding,
- },
- ),
- ),
- ) as _i21.ProcessResult);
- @override
- bool canRun(
- dynamic executable, {
- String? workingDirectory,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #canRun,
- [executable],
- {#workingDirectory: workingDirectory},
- ),
- returnValue: false,
- ) as bool);
- @override
- bool killPid(
- int? pid, [
- _i21.ProcessSignal? signal = _i21.ProcessSignal.sigterm,
- ]) =>
- (super.noSuchMethod(
- Invocation.method(
- #killPid,
- [
- pid,
- signal,
- ],
- ),
- returnValue: false,
- ) as bool);
-}
-
-/// A class which mocks [PullRequestsService].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockPullRequestsService extends _i1.Mock implements _i13.PullRequestsService {
- MockPullRequestsService() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i13.GitHub get github => (super.noSuchMethod(
- Invocation.getter(#github),
- returnValue: _FakeGitHub_16(
- this,
- Invocation.getter(#github),
- ),
- ) as _i13.GitHub);
- @override
- _i19.Stream<_i13.PullRequest> list(
- _i13.RepositorySlug? slug, {
- int? pages,
- String? base,
- String? direction = r'desc',
- String? head,
- String? sort = r'created',
- String? state = r'open',
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #list,
- [slug],
- {
- #pages: pages,
- #base: base,
- #direction: direction,
- #head: head,
- #sort: sort,
- #state: state,
- },
- ),
- returnValue: _i19.Stream<_i13.PullRequest>.empty(),
- ) as _i19.Stream<_i13.PullRequest>);
- @override
- _i19.Future<_i13.PullRequest> get(
- _i13.RepositorySlug? slug,
- int? number,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #get,
- [
- slug,
- number,
- ],
- ),
- returnValue: _i19.Future<_i13.PullRequest>.value(_FakePullRequest_32(
- this,
- Invocation.method(
- #get,
- [
- slug,
- number,
- ],
- ),
- )),
- ) as _i19.Future<_i13.PullRequest>);
- @override
- _i19.Future<_i13.PullRequest> create(
- _i13.RepositorySlug? slug,
- _i13.CreatePullRequest? request,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #create,
- [
- slug,
- request,
- ],
- ),
- returnValue: _i19.Future<_i13.PullRequest>.value(_FakePullRequest_32(
- this,
- Invocation.method(
- #create,
- [
- slug,
- request,
- ],
- ),
- )),
- ) as _i19.Future<_i13.PullRequest>);
- @override
- _i19.Future<_i13.PullRequest> edit(
- _i13.RepositorySlug? slug,
- int? number, {
- String? title,
- String? body,
- String? state,
- String? base,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #edit,
- [
- slug,
- number,
- ],
- {
- #title: title,
- #body: body,
- #state: state,
- #base: base,
- },
- ),
- returnValue: _i19.Future<_i13.PullRequest>.value(_FakePullRequest_32(
- this,
- Invocation.method(
- #edit,
- [
- slug,
- number,
- ],
- {
- #title: title,
- #body: body,
- #state: state,
- #base: base,
- },
- ),
- )),
- ) as _i19.Future<_i13.PullRequest>);
- @override
- _i19.Stream<_i13.RepositoryCommit> listCommits(
- _i13.RepositorySlug? slug,
- int? number,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #listCommits,
- [
- slug,
- number,
- ],
- ),
- returnValue: _i19.Stream<_i13.RepositoryCommit>.empty(),
- ) as _i19.Stream<_i13.RepositoryCommit>);
- @override
- _i19.Stream<_i13.PullRequestFile> listFiles(
- _i13.RepositorySlug? slug,
- int? number,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #listFiles,
- [
- slug,
- number,
- ],
- ),
- returnValue: _i19.Stream<_i13.PullRequestFile>.empty(),
- ) as _i19.Stream<_i13.PullRequestFile>);
- @override
- _i19.Stream<_i13.PullRequestReview> listReviews(
- _i13.RepositorySlug? slug,
- int? number,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #listReviews,
- [
- slug,
- number,
- ],
- ),
- returnValue: _i19.Stream<_i13.PullRequestReview>.empty(),
- ) as _i19.Stream<_i13.PullRequestReview>);
- @override
- _i19.Future<bool> isMerged(
- _i13.RepositorySlug? slug,
- int? number,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #isMerged,
- [
- slug,
- number,
- ],
- ),
- returnValue: _i19.Future<bool>.value(false),
- ) as _i19.Future<bool>);
- @override
- _i19.Future<_i13.PullRequestMerge> merge(
- _i13.RepositorySlug? slug,
- int? number, {
- String? message,
- _i13.MergeMethod? mergeMethod = _i13.MergeMethod.merge,
- String? requestSha,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #merge,
- [
- slug,
- number,
- ],
- {
- #message: message,
- #mergeMethod: mergeMethod,
- #requestSha: requestSha,
- },
- ),
- returnValue: _i19.Future<_i13.PullRequestMerge>.value(_FakePullRequestMerge_61(
- this,
- Invocation.method(
- #merge,
- [
- slug,
- number,
- ],
- {
- #message: message,
- #mergeMethod: mergeMethod,
- #requestSha: requestSha,
- },
- ),
- )),
- ) as _i19.Future<_i13.PullRequestMerge>);
- @override
- _i19.Stream<_i13.PullRequestComment> listCommentsByPullRequest(
- _i13.RepositorySlug? slug,
- int? number,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #listCommentsByPullRequest,
- [
- slug,
- number,
- ],
- ),
- returnValue: _i19.Stream<_i13.PullRequestComment>.empty(),
- ) as _i19.Stream<_i13.PullRequestComment>);
- @override
- _i19.Stream<_i13.PullRequestComment> listComments(_i13.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #listComments,
- [slug],
- ),
- returnValue: _i19.Stream<_i13.PullRequestComment>.empty(),
- ) as _i19.Stream<_i13.PullRequestComment>);
- @override
- _i19.Future<_i13.PullRequestComment> createComment(
- _i13.RepositorySlug? slug,
- int? number,
- _i13.CreatePullRequestComment? comment,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #createComment,
- [
- slug,
- number,
- comment,
- ],
- ),
- returnValue: _i19.Future<_i13.PullRequestComment>.value(_FakePullRequestComment_62(
- this,
- Invocation.method(
- #createComment,
- [
- slug,
- number,
- comment,
- ],
- ),
- )),
- ) as _i19.Future<_i13.PullRequestComment>);
- @override
- _i19.Future<_i13.PullRequestReview> createReview(
- _i13.RepositorySlug? slug,
- _i13.CreatePullRequestReview? review,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #createReview,
- [
- slug,
- review,
- ],
- ),
- returnValue: _i19.Future<_i13.PullRequestReview>.value(_FakePullRequestReview_63(
- this,
- Invocation.method(
- #createReview,
- [
- slug,
- review,
- ],
- ),
- )),
- ) as _i19.Future<_i13.PullRequestReview>);
-}
-
-/// A class which mocks [RepositoriesService].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockRepositoriesService extends _i1.Mock implements _i13.RepositoriesService {
- MockRepositoriesService() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i13.GitHub get github => (super.noSuchMethod(
- Invocation.getter(#github),
- returnValue: _FakeGitHub_16(
- this,
- Invocation.getter(#github),
- ),
- ) as _i13.GitHub);
- @override
- _i19.Stream<_i13.Repository> listRepositories({
- String? type = r'owner',
- String? sort = r'full_name',
- String? direction = r'asc',
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #listRepositories,
- [],
- {
- #type: type,
- #sort: sort,
- #direction: direction,
- },
- ),
- returnValue: _i19.Stream<_i13.Repository>.empty(),
- ) as _i19.Stream<_i13.Repository>);
- @override
- _i19.Stream<_i13.Repository> listUserRepositories(
- String? user, {
- String? type = r'owner',
- String? sort = r'full_name',
- String? direction = r'asc',
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #listUserRepositories,
- [user],
- {
- #type: type,
- #sort: sort,
- #direction: direction,
- },
- ),
- returnValue: _i19.Stream<_i13.Repository>.empty(),
- ) as _i19.Stream<_i13.Repository>);
- @override
- _i19.Stream<_i13.Repository> listOrganizationRepositories(
- String? org, {
- String? type = r'all',
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #listOrganizationRepositories,
- [org],
- {#type: type},
- ),
- returnValue: _i19.Stream<_i13.Repository>.empty(),
- ) as _i19.Stream<_i13.Repository>);
- @override
- _i19.Stream<_i13.Repository> listPublicRepositories({
- int? limit = 50,
- DateTime? since,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #listPublicRepositories,
- [],
- {
- #limit: limit,
- #since: since,
- },
- ),
- returnValue: _i19.Stream<_i13.Repository>.empty(),
- ) as _i19.Stream<_i13.Repository>);
- @override
- _i19.Future<_i13.Repository> createRepository(
- _i13.CreateRepository? repository, {
- String? org,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #createRepository,
- [repository],
- {#org: org},
- ),
- returnValue: _i19.Future<_i13.Repository>.value(_FakeRepository_64(
- this,
- Invocation.method(
- #createRepository,
- [repository],
- {#org: org},
- ),
- )),
- ) as _i19.Future<_i13.Repository>);
- @override
- _i19.Future<_i13.LicenseDetails> getLicense(_i13.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #getLicense,
- [slug],
- ),
- returnValue: _i19.Future<_i13.LicenseDetails>.value(_FakeLicenseDetails_65(
- this,
- Invocation.method(
- #getLicense,
- [slug],
- ),
- )),
- ) as _i19.Future<_i13.LicenseDetails>);
- @override
- _i19.Future<_i13.Repository> getRepository(_i13.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #getRepository,
- [slug],
- ),
- returnValue: _i19.Future<_i13.Repository>.value(_FakeRepository_64(
- this,
- Invocation.method(
- #getRepository,
- [slug],
- ),
- )),
- ) as _i19.Future<_i13.Repository>);
- @override
- _i19.Stream<_i13.Repository> getRepositories(List<_i13.RepositorySlug>? slugs) => (super.noSuchMethod(
- Invocation.method(
- #getRepositories,
- [slugs],
- ),
- returnValue: _i19.Stream<_i13.Repository>.empty(),
- ) as _i19.Stream<_i13.Repository>);
- @override
- _i19.Future<_i13.Repository> editRepository(
- _i13.RepositorySlug? slug, {
- String? name,
- String? description,
- String? homepage,
- bool? private,
- bool? hasIssues,
- bool? hasWiki,
- bool? hasDownloads,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #editRepository,
- [slug],
- {
- #name: name,
- #description: description,
- #homepage: homepage,
- #private: private,
- #hasIssues: hasIssues,
- #hasWiki: hasWiki,
- #hasDownloads: hasDownloads,
- },
- ),
- returnValue: _i19.Future<_i13.Repository>.value(_FakeRepository_64(
- this,
- Invocation.method(
- #editRepository,
- [slug],
- {
- #name: name,
- #description: description,
- #homepage: homepage,
- #private: private,
- #hasIssues: hasIssues,
- #hasWiki: hasWiki,
- #hasDownloads: hasDownloads,
- },
- ),
- )),
- ) as _i19.Future<_i13.Repository>);
- @override
- _i19.Future<bool> deleteRepository(_i13.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #deleteRepository,
- [slug],
- ),
- returnValue: _i19.Future<bool>.value(false),
- ) as _i19.Future<bool>);
- @override
- _i19.Stream<_i13.Contributor> listContributors(
- _i13.RepositorySlug? slug, {
- bool? anon = false,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #listContributors,
- [slug],
- {#anon: anon},
- ),
- returnValue: _i19.Stream<_i13.Contributor>.empty(),
- ) as _i19.Stream<_i13.Contributor>);
- @override
- _i19.Stream<_i13.Team> listTeams(_i13.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #listTeams,
- [slug],
- ),
- returnValue: _i19.Stream<_i13.Team>.empty(),
- ) as _i19.Stream<_i13.Team>);
- @override
- _i19.Future<_i13.LanguageBreakdown> listLanguages(_i13.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #listLanguages,
- [slug],
- ),
- returnValue: _i19.Future<_i13.LanguageBreakdown>.value(_FakeLanguageBreakdown_66(
- this,
- Invocation.method(
- #listLanguages,
- [slug],
- ),
- )),
- ) as _i19.Future<_i13.LanguageBreakdown>);
- @override
- _i19.Stream<_i13.Tag> listTags(
- _i13.RepositorySlug? slug, {
- int? page = 1,
- int? pages,
- int? perPage = 30,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #listTags,
- [slug],
- {
- #page: page,
- #pages: pages,
- #perPage: perPage,
- },
- ),
- returnValue: _i19.Stream<_i13.Tag>.empty(),
- ) as _i19.Stream<_i13.Tag>);
- @override
- _i19.Stream<_i13.Branch> listBranches(_i13.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #listBranches,
- [slug],
- ),
- returnValue: _i19.Stream<_i13.Branch>.empty(),
- ) as _i19.Stream<_i13.Branch>);
- @override
- _i19.Future<_i13.Branch> getBranch(
- _i13.RepositorySlug? slug,
- String? branch,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #getBranch,
- [
- slug,
- branch,
- ],
- ),
- returnValue: _i19.Future<_i13.Branch>.value(_FakeBranch_67(
- this,
- Invocation.method(
- #getBranch,
- [
- slug,
- branch,
- ],
- ),
- )),
- ) as _i19.Future<_i13.Branch>);
- @override
- _i19.Stream<_i13.Collaborator> listCollaborators(_i13.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #listCollaborators,
- [slug],
- ),
- returnValue: _i19.Stream<_i13.Collaborator>.empty(),
- ) as _i19.Stream<_i13.Collaborator>);
- @override
- _i19.Future<bool> isCollaborator(
- _i13.RepositorySlug? slug,
- String? user,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #isCollaborator,
- [
- slug,
- user,
- ],
- ),
- returnValue: _i19.Future<bool>.value(false),
- ) as _i19.Future<bool>);
- @override
- _i19.Future<bool> addCollaborator(
- _i13.RepositorySlug? slug,
- String? user,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #addCollaborator,
- [
- slug,
- user,
- ],
- ),
- returnValue: _i19.Future<bool>.value(false),
- ) as _i19.Future<bool>);
- @override
- _i19.Future<bool> removeCollaborator(
- _i13.RepositorySlug? slug,
- String? user,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #removeCollaborator,
- [
- slug,
- user,
- ],
- ),
- returnValue: _i19.Future<bool>.value(false),
- ) as _i19.Future<bool>);
- @override
- _i19.Stream<_i13.CommitComment> listSingleCommitComments(
- _i13.RepositorySlug? slug,
- _i13.RepositoryCommit? commit,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #listSingleCommitComments,
- [
- slug,
- commit,
- ],
- ),
- returnValue: _i19.Stream<_i13.CommitComment>.empty(),
- ) as _i19.Stream<_i13.CommitComment>);
- @override
- _i19.Stream<_i13.CommitComment> listCommitComments(_i13.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #listCommitComments,
- [slug],
- ),
- returnValue: _i19.Stream<_i13.CommitComment>.empty(),
- ) as _i19.Stream<_i13.CommitComment>);
- @override
- _i19.Future<_i13.CommitComment> createCommitComment(
- _i13.RepositorySlug? slug,
- _i13.RepositoryCommit? commit, {
- required String? body,
- String? path,
- int? position,
- int? line,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #createCommitComment,
- [
- slug,
- commit,
- ],
- {
- #body: body,
- #path: path,
- #position: position,
- #line: line,
- },
- ),
- returnValue: _i19.Future<_i13.CommitComment>.value(_FakeCommitComment_68(
- this,
- Invocation.method(
- #createCommitComment,
- [
- slug,
- commit,
- ],
- {
- #body: body,
- #path: path,
- #position: position,
- #line: line,
- },
- ),
- )),
- ) as _i19.Future<_i13.CommitComment>);
- @override
- _i19.Future<_i13.CommitComment> getCommitComment(
- _i13.RepositorySlug? slug, {
- required int? id,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #getCommitComment,
- [slug],
- {#id: id},
- ),
- returnValue: _i19.Future<_i13.CommitComment>.value(_FakeCommitComment_68(
- this,
- Invocation.method(
- #getCommitComment,
- [slug],
- {#id: id},
- ),
- )),
- ) as _i19.Future<_i13.CommitComment>);
- @override
- _i19.Future<_i13.CommitComment> updateCommitComment(
- _i13.RepositorySlug? slug, {
- required int? id,
- required String? body,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #updateCommitComment,
- [slug],
- {
- #id: id,
- #body: body,
- },
- ),
- returnValue: _i19.Future<_i13.CommitComment>.value(_FakeCommitComment_68(
- this,
- Invocation.method(
- #updateCommitComment,
- [slug],
- {
- #id: id,
- #body: body,
- },
- ),
- )),
- ) as _i19.Future<_i13.CommitComment>);
- @override
- _i19.Future<bool> deleteCommitComment(
- _i13.RepositorySlug? slug, {
- required int? id,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #deleteCommitComment,
- [slug],
- {#id: id},
- ),
- returnValue: _i19.Future<bool>.value(false),
- ) as _i19.Future<bool>);
- @override
- _i19.Stream<_i13.RepositoryCommit> listCommits(
- _i13.RepositorySlug? slug, {
- String? sha,
- String? path,
- String? author,
- String? committer,
- DateTime? since,
- DateTime? until,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #listCommits,
- [slug],
- {
- #sha: sha,
- #path: path,
- #author: author,
- #committer: committer,
- #since: since,
- #until: until,
- },
- ),
- returnValue: _i19.Stream<_i13.RepositoryCommit>.empty(),
- ) as _i19.Stream<_i13.RepositoryCommit>);
- @override
- _i19.Future<_i13.RepositoryCommit> getCommit(
- _i13.RepositorySlug? slug,
- String? sha,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #getCommit,
- [
- slug,
- sha,
- ],
- ),
- returnValue: _i19.Future<_i13.RepositoryCommit>.value(_FakeRepositoryCommit_69(
- this,
- Invocation.method(
- #getCommit,
- [
- slug,
- sha,
- ],
- ),
- )),
- ) as _i19.Future<_i13.RepositoryCommit>);
- @override
- _i19.Future<String> getCommitDiff(
- _i13.RepositorySlug? slug,
- String? sha,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #getCommitDiff,
- [
- slug,
- sha,
- ],
- ),
- returnValue: _i19.Future<String>.value(''),
- ) as _i19.Future<String>);
- @override
- _i19.Future<_i13.GitHubComparison> compareCommits(
- _i13.RepositorySlug? slug,
- String? refBase,
- String? refHead,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #compareCommits,
- [
- slug,
- refBase,
- refHead,
- ],
- ),
- returnValue: _i19.Future<_i13.GitHubComparison>.value(_FakeGitHubComparison_70(
- this,
- Invocation.method(
- #compareCommits,
- [
- slug,
- refBase,
- refHead,
- ],
- ),
- )),
- ) as _i19.Future<_i13.GitHubComparison>);
- @override
- _i19.Future<_i13.GitHubFile> getReadme(
- _i13.RepositorySlug? slug, {
- String? ref,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #getReadme,
- [slug],
- {#ref: ref},
- ),
- returnValue: _i19.Future<_i13.GitHubFile>.value(_FakeGitHubFile_71(
- this,
- Invocation.method(
- #getReadme,
- [slug],
- {#ref: ref},
- ),
- )),
- ) as _i19.Future<_i13.GitHubFile>);
- @override
- _i19.Future<_i13.RepositoryContents> getContents(
- _i13.RepositorySlug? slug,
- String? path, {
- String? ref,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #getContents,
- [
- slug,
- path,
- ],
- {#ref: ref},
- ),
- returnValue: _i19.Future<_i13.RepositoryContents>.value(_FakeRepositoryContents_72(
- this,
- Invocation.method(
- #getContents,
- [
- slug,
- path,
- ],
- {#ref: ref},
- ),
- )),
- ) as _i19.Future<_i13.RepositoryContents>);
- @override
- _i19.Future<_i13.ContentCreation> createFile(
- _i13.RepositorySlug? slug,
- _i13.CreateFile? file,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #createFile,
- [
- slug,
- file,
- ],
- ),
- returnValue: _i19.Future<_i13.ContentCreation>.value(_FakeContentCreation_73(
- this,
- Invocation.method(
- #createFile,
- [
- slug,
- file,
- ],
- ),
- )),
- ) as _i19.Future<_i13.ContentCreation>);
- @override
- _i19.Future<_i13.ContentCreation> updateFile(
- _i13.RepositorySlug? slug,
- String? path,
- String? message,
- String? content,
- String? sha, {
- String? branch,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #updateFile,
- [
- slug,
- path,
- message,
- content,
- sha,
- ],
- {#branch: branch},
- ),
- returnValue: _i19.Future<_i13.ContentCreation>.value(_FakeContentCreation_73(
- this,
- Invocation.method(
- #updateFile,
- [
- slug,
- path,
- message,
- content,
- sha,
- ],
- {#branch: branch},
- ),
- )),
- ) as _i19.Future<_i13.ContentCreation>);
- @override
- _i19.Future<_i13.ContentCreation> deleteFile(
- _i13.RepositorySlug? slug,
- String? path,
- String? message,
- String? sha,
- String? branch,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #deleteFile,
- [
- slug,
- path,
- message,
- sha,
- branch,
- ],
- ),
- returnValue: _i19.Future<_i13.ContentCreation>.value(_FakeContentCreation_73(
- this,
- Invocation.method(
- #deleteFile,
- [
- slug,
- path,
- message,
- sha,
- branch,
- ],
- ),
- )),
- ) as _i19.Future<_i13.ContentCreation>);
- @override
- _i19.Future<String?> getArchiveLink(
- _i13.RepositorySlug? slug,
- String? ref, {
- String? format = r'tarball',
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #getArchiveLink,
- [
- slug,
- ref,
- ],
- {#format: format},
- ),
- returnValue: _i19.Future<String?>.value(),
- ) as _i19.Future<String?>);
- @override
- _i19.Stream<_i13.Repository> listForks(_i13.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #listForks,
- [slug],
- ),
- returnValue: _i19.Stream<_i13.Repository>.empty(),
- ) as _i19.Stream<_i13.Repository>);
- @override
- _i19.Future<_i13.Repository> createFork(
- _i13.RepositorySlug? slug, [
- _i13.CreateFork? fork,
- ]) =>
- (super.noSuchMethod(
- Invocation.method(
- #createFork,
- [
- slug,
- fork,
- ],
- ),
- returnValue: _i19.Future<_i13.Repository>.value(_FakeRepository_64(
- this,
- Invocation.method(
- #createFork,
- [
- slug,
- fork,
- ],
- ),
- )),
- ) as _i19.Future<_i13.Repository>);
- @override
- _i19.Stream<_i13.Hook> listHooks(_i13.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #listHooks,
- [slug],
- ),
- returnValue: _i19.Stream<_i13.Hook>.empty(),
- ) as _i19.Stream<_i13.Hook>);
- @override
- _i19.Future<_i13.Hook> getHook(
- _i13.RepositorySlug? slug,
- int? id,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #getHook,
- [
- slug,
- id,
- ],
- ),
- returnValue: _i19.Future<_i13.Hook>.value(_FakeHook_74(
- this,
- Invocation.method(
- #getHook,
- [
- slug,
- id,
- ],
- ),
- )),
- ) as _i19.Future<_i13.Hook>);
- @override
- _i19.Future<_i13.Hook> createHook(
- _i13.RepositorySlug? slug,
- _i13.CreateHook? hook,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #createHook,
- [
- slug,
- hook,
- ],
- ),
- returnValue: _i19.Future<_i13.Hook>.value(_FakeHook_74(
- this,
- Invocation.method(
- #createHook,
- [
- slug,
- hook,
- ],
- ),
- )),
- ) as _i19.Future<_i13.Hook>);
- @override
- _i19.Future<_i13.Hook> editHook(
- _i13.RepositorySlug? slug,
- _i13.Hook? hookToEdit, {
- String? configUrl,
- String? configContentType,
- String? configSecret,
- bool? configInsecureSsl,
- List<String>? events,
- List<String>? addEvents,
- List<String>? removeEvents,
- bool? active,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #editHook,
- [
- slug,
- hookToEdit,
- ],
- {
- #configUrl: configUrl,
- #configContentType: configContentType,
- #configSecret: configSecret,
- #configInsecureSsl: configInsecureSsl,
- #events: events,
- #addEvents: addEvents,
- #removeEvents: removeEvents,
- #active: active,
- },
- ),
- returnValue: _i19.Future<_i13.Hook>.value(_FakeHook_74(
- this,
- Invocation.method(
- #editHook,
- [
- slug,
- hookToEdit,
- ],
- {
- #configUrl: configUrl,
- #configContentType: configContentType,
- #configSecret: configSecret,
- #configInsecureSsl: configInsecureSsl,
- #events: events,
- #addEvents: addEvents,
- #removeEvents: removeEvents,
- #active: active,
- },
- ),
- )),
- ) as _i19.Future<_i13.Hook>);
- @override
- _i19.Future<bool> testPushHook(
- _i13.RepositorySlug? slug,
- int? id,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #testPushHook,
- [
- slug,
- id,
- ],
- ),
- returnValue: _i19.Future<bool>.value(false),
- ) as _i19.Future<bool>);
- @override
- _i19.Future<bool> pingHook(
- _i13.RepositorySlug? slug,
- int? id,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #pingHook,
- [
- slug,
- id,
- ],
- ),
- returnValue: _i19.Future<bool>.value(false),
- ) as _i19.Future<bool>);
- @override
- _i19.Future<bool> deleteHook(
- _i13.RepositorySlug? slug,
- int? id,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #deleteHook,
- [
- slug,
- id,
- ],
- ),
- returnValue: _i19.Future<bool>.value(false),
- ) as _i19.Future<bool>);
- @override
- _i19.Stream<_i13.PublicKey> listDeployKeys(_i13.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #listDeployKeys,
- [slug],
- ),
- returnValue: _i19.Stream<_i13.PublicKey>.empty(),
- ) as _i19.Stream<_i13.PublicKey>);
- @override
- _i19.Future<_i13.PublicKey> getDeployKey(
- _i13.RepositorySlug? slug, {
- required int? id,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #getDeployKey,
- [slug],
- {#id: id},
- ),
- returnValue: _i19.Future<_i13.PublicKey>.value(_FakePublicKey_75(
- this,
- Invocation.method(
- #getDeployKey,
- [slug],
- {#id: id},
- ),
- )),
- ) as _i19.Future<_i13.PublicKey>);
- @override
- _i19.Future<_i13.PublicKey> createDeployKey(
- _i13.RepositorySlug? slug,
- _i13.CreatePublicKey? key,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #createDeployKey,
- [
- slug,
- key,
- ],
- ),
- returnValue: _i19.Future<_i13.PublicKey>.value(_FakePublicKey_75(
- this,
- Invocation.method(
- #createDeployKey,
- [
- slug,
- key,
- ],
- ),
- )),
- ) as _i19.Future<_i13.PublicKey>);
- @override
- _i19.Future<bool> deleteDeployKey({
- required _i13.RepositorySlug? slug,
- required _i13.PublicKey? key,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #deleteDeployKey,
- [],
- {
- #slug: slug,
- #key: key,
- },
- ),
- returnValue: _i19.Future<bool>.value(false),
- ) as _i19.Future<bool>);
- @override
- _i19.Future<_i13.RepositoryCommit> merge(
- _i13.RepositorySlug? slug,
- _i13.CreateMerge? merge,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #merge,
- [
- slug,
- merge,
- ],
- ),
- returnValue: _i19.Future<_i13.RepositoryCommit>.value(_FakeRepositoryCommit_69(
- this,
- Invocation.method(
- #merge,
- [
- slug,
- merge,
- ],
- ),
- )),
- ) as _i19.Future<_i13.RepositoryCommit>);
- @override
- _i19.Future<_i13.RepositoryPages> getPagesInfo(_i13.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #getPagesInfo,
- [slug],
- ),
- returnValue: _i19.Future<_i13.RepositoryPages>.value(_FakeRepositoryPages_76(
- this,
- Invocation.method(
- #getPagesInfo,
- [slug],
- ),
- )),
- ) as _i19.Future<_i13.RepositoryPages>);
- @override
- _i19.Stream<_i13.PageBuild> listPagesBuilds(_i13.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #listPagesBuilds,
- [slug],
- ),
- returnValue: _i19.Stream<_i13.PageBuild>.empty(),
- ) as _i19.Stream<_i13.PageBuild>);
- @override
- _i19.Future<_i13.PageBuild> getLatestPagesBuild(_i13.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #getLatestPagesBuild,
- [slug],
- ),
- returnValue: _i19.Future<_i13.PageBuild>.value(_FakePageBuild_77(
- this,
- Invocation.method(
- #getLatestPagesBuild,
- [slug],
- ),
- )),
- ) as _i19.Future<_i13.PageBuild>);
- @override
- _i19.Stream<_i13.Release> listReleases(_i13.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #listReleases,
- [slug],
- ),
- returnValue: _i19.Stream<_i13.Release>.empty(),
- ) as _i19.Stream<_i13.Release>);
- @override
- _i19.Future<_i13.Release> getLatestRelease(_i13.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #getLatestRelease,
- [slug],
- ),
- returnValue: _i19.Future<_i13.Release>.value(_FakeRelease_78(
- this,
- Invocation.method(
- #getLatestRelease,
- [slug],
- ),
- )),
- ) as _i19.Future<_i13.Release>);
- @override
- _i19.Future<_i13.Release> getReleaseById(
- _i13.RepositorySlug? slug,
- int? id,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #getReleaseById,
- [
- slug,
- id,
- ],
- ),
- returnValue: _i19.Future<_i13.Release>.value(_FakeRelease_78(
- this,
- Invocation.method(
- #getReleaseById,
- [
- slug,
- id,
- ],
- ),
- )),
- ) as _i19.Future<_i13.Release>);
- @override
- _i19.Future<_i13.Release> getReleaseByTagName(
- _i13.RepositorySlug? slug,
- String? tagName,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #getReleaseByTagName,
- [
- slug,
- tagName,
- ],
- ),
- returnValue: _i19.Future<_i13.Release>.value(_FakeRelease_78(
- this,
- Invocation.method(
- #getReleaseByTagName,
- [
- slug,
- tagName,
- ],
- ),
- )),
- ) as _i19.Future<_i13.Release>);
- @override
- _i19.Future<_i13.Release> createRelease(
- _i13.RepositorySlug? slug,
- _i13.CreateRelease? createRelease, {
- bool? getIfExists = true,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #createRelease,
- [
- slug,
- createRelease,
- ],
- {#getIfExists: getIfExists},
- ),
- returnValue: _i19.Future<_i13.Release>.value(_FakeRelease_78(
- this,
- Invocation.method(
- #createRelease,
- [
- slug,
- createRelease,
- ],
- {#getIfExists: getIfExists},
- ),
- )),
- ) as _i19.Future<_i13.Release>);
- @override
- _i19.Future<_i13.Release> editRelease(
- _i13.RepositorySlug? slug,
- _i13.Release? releaseToEdit, {
- String? tagName,
- String? targetCommitish,
- String? name,
- String? body,
- bool? draft,
- bool? preRelease,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #editRelease,
- [
- slug,
- releaseToEdit,
- ],
- {
- #tagName: tagName,
- #targetCommitish: targetCommitish,
- #name: name,
- #body: body,
- #draft: draft,
- #preRelease: preRelease,
- },
- ),
- returnValue: _i19.Future<_i13.Release>.value(_FakeRelease_78(
- this,
- Invocation.method(
- #editRelease,
- [
- slug,
- releaseToEdit,
- ],
- {
- #tagName: tagName,
- #targetCommitish: targetCommitish,
- #name: name,
- #body: body,
- #draft: draft,
- #preRelease: preRelease,
- },
- ),
- )),
- ) as _i19.Future<_i13.Release>);
- @override
- _i19.Future<bool> deleteRelease(
- _i13.RepositorySlug? slug,
- _i13.Release? release,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #deleteRelease,
- [
- slug,
- release,
- ],
- ),
- returnValue: _i19.Future<bool>.value(false),
- ) as _i19.Future<bool>);
- @override
- _i19.Stream<_i13.ReleaseAsset> listReleaseAssets(
- _i13.RepositorySlug? slug,
- _i13.Release? release,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #listReleaseAssets,
- [
- slug,
- release,
- ],
- ),
- returnValue: _i19.Stream<_i13.ReleaseAsset>.empty(),
- ) as _i19.Stream<_i13.ReleaseAsset>);
- @override
- _i19.Future<_i13.ReleaseAsset> getReleaseAsset(
- _i13.RepositorySlug? slug,
- _i13.Release? release, {
- required int? assetId,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #getReleaseAsset,
- [
- slug,
- release,
- ],
- {#assetId: assetId},
- ),
- returnValue: _i19.Future<_i13.ReleaseAsset>.value(_FakeReleaseAsset_79(
- this,
- Invocation.method(
- #getReleaseAsset,
- [
- slug,
- release,
- ],
- {#assetId: assetId},
- ),
- )),
- ) as _i19.Future<_i13.ReleaseAsset>);
- @override
- _i19.Future<_i13.ReleaseAsset> editReleaseAsset(
- _i13.RepositorySlug? slug,
- _i13.ReleaseAsset? assetToEdit, {
- String? name,
- String? label,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #editReleaseAsset,
- [
- slug,
- assetToEdit,
- ],
- {
- #name: name,
- #label: label,
- },
- ),
- returnValue: _i19.Future<_i13.ReleaseAsset>.value(_FakeReleaseAsset_79(
- this,
- Invocation.method(
- #editReleaseAsset,
- [
- slug,
- assetToEdit,
- ],
- {
- #name: name,
- #label: label,
- },
- ),
- )),
- ) as _i19.Future<_i13.ReleaseAsset>);
- @override
- _i19.Future<bool> deleteReleaseAsset(
- _i13.RepositorySlug? slug,
- _i13.ReleaseAsset? asset,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #deleteReleaseAsset,
- [
- slug,
- asset,
- ],
- ),
- returnValue: _i19.Future<bool>.value(false),
- ) as _i19.Future<bool>);
- @override
- _i19.Future<List<_i13.ReleaseAsset>> uploadReleaseAssets(
- _i13.Release? release,
- Iterable<_i13.CreateReleaseAsset>? createReleaseAssets,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #uploadReleaseAssets,
- [
- release,
- createReleaseAssets,
- ],
- ),
- returnValue: _i19.Future<List<_i13.ReleaseAsset>>.value(<_i13.ReleaseAsset>[]),
- ) as _i19.Future<List<_i13.ReleaseAsset>>);
- @override
- _i19.Future<List<_i13.ContributorStatistics>> listContributorStats(_i13.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #listContributorStats,
- [slug],
- ),
- returnValue: _i19.Future<List<_i13.ContributorStatistics>>.value(<_i13.ContributorStatistics>[]),
- ) as _i19.Future<List<_i13.ContributorStatistics>>);
- @override
- _i19.Stream<_i13.YearCommitCountWeek> listCommitActivity(_i13.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #listCommitActivity,
- [slug],
- ),
- returnValue: _i19.Stream<_i13.YearCommitCountWeek>.empty(),
- ) as _i19.Stream<_i13.YearCommitCountWeek>);
- @override
- _i19.Stream<_i13.WeeklyChangesCount> listCodeFrequency(_i13.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #listCodeFrequency,
- [slug],
- ),
- returnValue: _i19.Stream<_i13.WeeklyChangesCount>.empty(),
- ) as _i19.Stream<_i13.WeeklyChangesCount>);
- @override
- _i19.Future<_i13.ContributorParticipation> getParticipation(_i13.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #getParticipation,
- [slug],
- ),
- returnValue: _i19.Future<_i13.ContributorParticipation>.value(_FakeContributorParticipation_80(
- this,
- Invocation.method(
- #getParticipation,
- [slug],
- ),
- )),
- ) as _i19.Future<_i13.ContributorParticipation>);
- @override
- _i19.Stream<_i13.PunchcardEntry> listPunchcard(_i13.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #listPunchcard,
- [slug],
- ),
- returnValue: _i19.Stream<_i13.PunchcardEntry>.empty(),
- ) as _i19.Stream<_i13.PunchcardEntry>);
- @override
- _i19.Stream<_i13.RepositoryStatus> listStatuses(
- _i13.RepositorySlug? slug,
- String? ref,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #listStatuses,
- [
- slug,
- ref,
- ],
- ),
- returnValue: _i19.Stream<_i13.RepositoryStatus>.empty(),
- ) as _i19.Stream<_i13.RepositoryStatus>);
- @override
- _i19.Future<_i13.RepositoryStatus> createStatus(
- _i13.RepositorySlug? slug,
- String? ref,
- _i13.CreateStatus? request,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #createStatus,
- [
- slug,
- ref,
- request,
- ],
- ),
- returnValue: _i19.Future<_i13.RepositoryStatus>.value(_FakeRepositoryStatus_81(
- this,
- Invocation.method(
- #createStatus,
- [
- slug,
- ref,
- request,
- ],
- ),
- )),
- ) as _i19.Future<_i13.RepositoryStatus>);
- @override
- _i19.Future<_i13.CombinedRepositoryStatus> getCombinedStatus(
- _i13.RepositorySlug? slug,
- String? ref,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #getCombinedStatus,
- [
- slug,
- ref,
- ],
- ),
- returnValue: _i19.Future<_i13.CombinedRepositoryStatus>.value(_FakeCombinedRepositoryStatus_82(
- this,
- Invocation.method(
- #getCombinedStatus,
- [
- slug,
- ref,
- ],
- ),
- )),
- ) as _i19.Future<_i13.CombinedRepositoryStatus>);
- @override
- _i19.Future<_i13.ReleaseNotes> generateReleaseNotes(_i13.CreateReleaseNotes? crn) => (super.noSuchMethod(
- Invocation.method(
- #generateReleaseNotes,
- [crn],
- ),
- returnValue: _i19.Future<_i13.ReleaseNotes>.value(_FakeReleaseNotes_83(
- this,
- Invocation.method(
- #generateReleaseNotes,
- [crn],
- ),
- )),
- ) as _i19.Future<_i13.ReleaseNotes>);
-}
-
-/// A class which mocks [SearchService].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockSearchService extends _i1.Mock implements _i13.SearchService {
- MockSearchService() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i13.GitHub get github => (super.noSuchMethod(
- Invocation.getter(#github),
- returnValue: _FakeGitHub_16(
- this,
- Invocation.getter(#github),
- ),
- ) as _i13.GitHub);
- @override
- _i19.Stream<_i13.Repository> repositories(
- String? query, {
- String? sort,
- int? pages = 2,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #repositories,
- [query],
- {
- #sort: sort,
- #pages: pages,
- },
- ),
- returnValue: _i19.Stream<_i13.Repository>.empty(),
- ) as _i19.Stream<_i13.Repository>);
- @override
- _i19.Stream<_i13.CodeSearchResults> code(
- String? query, {
- int? pages,
- int? perPage,
- String? language,
- String? filename,
- String? extension,
- String? user,
- String? org,
- String? repo,
- String? fork,
- String? path,
- String? size,
- bool? inFile = true,
- bool? inPath = false,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #code,
- [query],
- {
- #pages: pages,
- #perPage: perPage,
- #language: language,
- #filename: filename,
- #extension: extension,
- #user: user,
- #org: org,
- #repo: repo,
- #fork: fork,
- #path: path,
- #size: size,
- #inFile: inFile,
- #inPath: inPath,
- },
- ),
- returnValue: _i19.Stream<_i13.CodeSearchResults>.empty(),
- ) as _i19.Stream<_i13.CodeSearchResults>);
- @override
- _i19.Stream<_i13.Issue> issues(
- String? query, {
- String? sort,
- int? pages = 2,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #issues,
- [query],
- {
- #sort: sort,
- #pages: pages,
- },
- ),
- returnValue: _i19.Stream<_i13.Issue>.empty(),
- ) as _i19.Stream<_i13.Issue>);
- @override
- _i19.Stream<_i13.User> users(
- String? query, {
- String? sort,
- int? pages = 2,
- int? perPage = 30,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #users,
- [query],
- {
- #sort: sort,
- #pages: pages,
- #perPage: perPage,
- },
- ),
- returnValue: _i19.Stream<_i13.User>.empty(),
- ) as _i19.Stream<_i13.User>);
-}
-
-/// A class which mocks [TabledataResource].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockTabledataResource extends _i1.Mock implements _i6.TabledataResource {
- MockTabledataResource() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i19.Future<_i6.TableDataInsertAllResponse> insertAll(
- _i6.TableDataInsertAllRequest? request,
- String? projectId,
- String? datasetId,
- String? tableId, {
- String? $fields,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #insertAll,
- [
- request,
- projectId,
- datasetId,
- tableId,
- ],
- {#$fields: $fields},
- ),
- returnValue: _i19.Future<_i6.TableDataInsertAllResponse>.value(_FakeTableDataInsertAllResponse_84(
- this,
- Invocation.method(
- #insertAll,
- [
- request,
- projectId,
- datasetId,
- tableId,
- ],
- {#$fields: $fields},
- ),
- )),
- ) as _i19.Future<_i6.TableDataInsertAllResponse>);
- @override
- _i19.Future<_i6.TableDataList> list(
- String? projectId,
- String? datasetId,
- String? tableId, {
- int? maxResults,
- String? pageToken,
- String? selectedFields,
- String? startIndex,
- String? $fields,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #list,
- [
- projectId,
- datasetId,
- tableId,
- ],
- {
- #maxResults: maxResults,
- #pageToken: pageToken,
- #selectedFields: selectedFields,
- #startIndex: startIndex,
- #$fields: $fields,
- },
- ),
- returnValue: _i19.Future<_i6.TableDataList>.value(_FakeTableDataList_85(
- this,
- Invocation.method(
- #list,
- [
- projectId,
- datasetId,
- tableId,
- ],
- {
- #maxResults: maxResults,
- #pageToken: pageToken,
- #selectedFields: selectedFields,
- #startIndex: startIndex,
- #$fields: $fields,
- },
- ),
- )),
- ) as _i19.Future<_i6.TableDataList>);
-}
-
-/// A class which mocks [UsersService].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockUsersService extends _i1.Mock implements _i13.UsersService {
- MockUsersService() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i13.GitHub get github => (super.noSuchMethod(
- Invocation.getter(#github),
- returnValue: _FakeGitHub_16(
- this,
- Invocation.getter(#github),
- ),
- ) as _i13.GitHub);
- @override
- _i19.Future<_i13.User> getUser(String? name) => (super.noSuchMethod(
- Invocation.method(
- #getUser,
- [name],
- ),
- returnValue: _i19.Future<_i13.User>.value(_FakeUser_86(
- this,
- Invocation.method(
- #getUser,
- [name],
- ),
- )),
- ) as _i19.Future<_i13.User>);
- @override
- _i19.Future<_i13.CurrentUser> editCurrentUser({
- String? name,
- String? email,
- String? blog,
- String? company,
- String? location,
- bool? hireable,
- String? bio,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #editCurrentUser,
- [],
- {
- #name: name,
- #email: email,
- #blog: blog,
- #company: company,
- #location: location,
- #hireable: hireable,
- #bio: bio,
- },
- ),
- returnValue: _i19.Future<_i13.CurrentUser>.value(_FakeCurrentUser_87(
- this,
- Invocation.method(
- #editCurrentUser,
- [],
- {
- #name: name,
- #email: email,
- #blog: blog,
- #company: company,
- #location: location,
- #hireable: hireable,
- #bio: bio,
- },
- ),
- )),
- ) as _i19.Future<_i13.CurrentUser>);
- @override
- _i19.Stream<_i13.User> getUsers(
- List<String>? names, {
- int? pages,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #getUsers,
- [names],
- {#pages: pages},
- ),
- returnValue: _i19.Stream<_i13.User>.empty(),
- ) as _i19.Stream<_i13.User>);
- @override
- _i19.Future<_i13.CurrentUser> getCurrentUser() => (super.noSuchMethod(
- Invocation.method(
- #getCurrentUser,
- [],
- ),
- returnValue: _i19.Future<_i13.CurrentUser>.value(_FakeCurrentUser_87(
- this,
- Invocation.method(
- #getCurrentUser,
- [],
- ),
- )),
- ) as _i19.Future<_i13.CurrentUser>);
- @override
- _i19.Future<bool> isUser(String? name) => (super.noSuchMethod(
- Invocation.method(
- #isUser,
- [name],
- ),
- returnValue: _i19.Future<bool>.value(false),
- ) as _i19.Future<bool>);
- @override
- _i19.Stream<_i13.User> listUsers({
- int? pages,
- int? since,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #listUsers,
- [],
- {
- #pages: pages,
- #since: since,
- },
- ),
- returnValue: _i19.Stream<_i13.User>.empty(),
- ) as _i19.Stream<_i13.User>);
- @override
- _i19.Stream<_i13.UserEmail> listEmails() => (super.noSuchMethod(
- Invocation.method(
- #listEmails,
- [],
- ),
- returnValue: _i19.Stream<_i13.UserEmail>.empty(),
- ) as _i19.Stream<_i13.UserEmail>);
- @override
- _i19.Stream<_i13.UserEmail> addEmails(List<String>? emails) => (super.noSuchMethod(
- Invocation.method(
- #addEmails,
- [emails],
- ),
- returnValue: _i19.Stream<_i13.UserEmail>.empty(),
- ) as _i19.Stream<_i13.UserEmail>);
- @override
- _i19.Future<bool> deleteEmails(List<String>? emails) => (super.noSuchMethod(
- Invocation.method(
- #deleteEmails,
- [emails],
- ),
- returnValue: _i19.Future<bool>.value(false),
- ) as _i19.Future<bool>);
- @override
- _i19.Stream<_i13.User> listUserFollowers(String? user) => (super.noSuchMethod(
- Invocation.method(
- #listUserFollowers,
- [user],
- ),
- returnValue: _i19.Stream<_i13.User>.empty(),
- ) as _i19.Stream<_i13.User>);
- @override
- _i19.Future<bool> isFollowingUser(String? user) => (super.noSuchMethod(
- Invocation.method(
- #isFollowingUser,
- [user],
- ),
- returnValue: _i19.Future<bool>.value(false),
- ) as _i19.Future<bool>);
- @override
- _i19.Future<bool> isUserFollowing(
- String? user,
- String? target,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #isUserFollowing,
- [
- user,
- target,
- ],
- ),
- returnValue: _i19.Future<bool>.value(false),
- ) as _i19.Future<bool>);
- @override
- _i19.Future<bool> followUser(String? user) => (super.noSuchMethod(
- Invocation.method(
- #followUser,
- [user],
- ),
- returnValue: _i19.Future<bool>.value(false),
- ) as _i19.Future<bool>);
- @override
- _i19.Future<bool> unfollowUser(String? user) => (super.noSuchMethod(
- Invocation.method(
- #unfollowUser,
- [user],
- ),
- returnValue: _i19.Future<bool>.value(false),
- ) as _i19.Future<bool>);
- @override
- _i19.Stream<_i13.User> listCurrentUserFollowers() => (super.noSuchMethod(
- Invocation.method(
- #listCurrentUserFollowers,
- [],
- ),
- returnValue: _i19.Stream<_i13.User>.empty(),
- ) as _i19.Stream<_i13.User>);
- @override
- _i19.Stream<_i13.User> listCurrentUserFollowing() => (super.noSuchMethod(
- Invocation.method(
- #listCurrentUserFollowing,
- [],
- ),
- returnValue: _i19.Stream<_i13.User>.empty(),
- ) as _i19.Stream<_i13.User>);
- @override
- _i19.Stream<_i13.PublicKey> listPublicKeys([String? userLogin]) => (super.noSuchMethod(
- Invocation.method(
- #listPublicKeys,
- [userLogin],
- ),
- returnValue: _i19.Stream<_i13.PublicKey>.empty(),
- ) as _i19.Stream<_i13.PublicKey>);
- @override
- _i19.Future<_i13.PublicKey> createPublicKey(_i13.CreatePublicKey? key) => (super.noSuchMethod(
- Invocation.method(
- #createPublicKey,
- [key],
- ),
- returnValue: _i19.Future<_i13.PublicKey>.value(_FakePublicKey_75(
- this,
- Invocation.method(
- #createPublicKey,
- [key],
- ),
- )),
- ) as _i19.Future<_i13.PublicKey>);
-}
-
-/// A class which mocks [Cache].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockCache extends _i1.Mock implements _i24.Cache<_i35.Uint8List> {
- MockCache() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i24.Entry<_i35.Uint8List> operator [](String? key) => (super.noSuchMethod(
- Invocation.method(
- #[],
- [key],
- ),
- returnValue: _FakeEntry_88<_i35.Uint8List>(
- this,
- Invocation.method(
- #[],
- [key],
- ),
- ),
- ) as _i24.Entry<_i35.Uint8List>);
- @override
- _i24.Cache<_i35.Uint8List> withPrefix(String? prefix) => (super.noSuchMethod(
- Invocation.method(
- #withPrefix,
- [prefix],
- ),
- returnValue: _FakeCache_89<_i35.Uint8List>(
- this,
- Invocation.method(
- #withPrefix,
- [prefix],
- ),
- ),
- ) as _i24.Cache<_i35.Uint8List>);
- @override
- _i24.Cache<S> withCodec<S>(_i22.Codec<S, _i35.Uint8List>? codec) => (super.noSuchMethod(
- Invocation.method(
- #withCodec,
- [codec],
- ),
- returnValue: _FakeCache_89<S>(
- this,
- Invocation.method(
- #withCodec,
- [codec],
- ),
- ),
- ) as _i24.Cache<S>);
- @override
- _i24.Cache<_i35.Uint8List> withTTL(Duration? ttl) => (super.noSuchMethod(
- Invocation.method(
- #withTTL,
- [ttl],
- ),
- returnValue: _FakeCache_89<_i35.Uint8List>(
- this,
- Invocation.method(
- #withTTL,
- [ttl],
- ),
- ),
- ) as _i24.Cache<_i35.Uint8List>);
-}
-
-/// A class which mocks [GitHub].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockGitHub extends _i1.Mock implements _i13.GitHub {
- MockGitHub() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i13.Authentication get auth => (super.noSuchMethod(
- Invocation.getter(#auth),
- returnValue: _FakeAuthentication_90(
- this,
- Invocation.getter(#auth),
- ),
- ) as _i13.Authentication);
- @override
- set auth(_i13.Authentication? _auth) => super.noSuchMethod(
- Invocation.setter(
- #auth,
- _auth,
- ),
- returnValueForMissingStub: null,
- );
- @override
- String get endpoint => (super.noSuchMethod(
- Invocation.getter(#endpoint),
- returnValue: '',
- ) as String);
- @override
- String get version => (super.noSuchMethod(
- Invocation.getter(#version),
- returnValue: '',
- ) as String);
- @override
- _i2.Client get client => (super.noSuchMethod(
- Invocation.getter(#client),
- returnValue: _FakeClient_0(
- this,
- Invocation.getter(#client),
- ),
- ) as _i2.Client);
- @override
- _i13.ActivityService get activity => (super.noSuchMethod(
- Invocation.getter(#activity),
- returnValue: _FakeActivityService_91(
- this,
- Invocation.getter(#activity),
- ),
- ) as _i13.ActivityService);
- @override
- _i13.AuthorizationsService get authorizations => (super.noSuchMethod(
- Invocation.getter(#authorizations),
- returnValue: _FakeAuthorizationsService_92(
- this,
- Invocation.getter(#authorizations),
- ),
- ) as _i13.AuthorizationsService);
- @override
- _i13.GistsService get gists => (super.noSuchMethod(
- Invocation.getter(#gists),
- returnValue: _FakeGistsService_93(
- this,
- Invocation.getter(#gists),
- ),
- ) as _i13.GistsService);
- @override
- _i13.GitService get git => (super.noSuchMethod(
- Invocation.getter(#git),
- returnValue: _FakeGitService_94(
- this,
- Invocation.getter(#git),
- ),
- ) as _i13.GitService);
- @override
- _i13.IssuesService get issues => (super.noSuchMethod(
- Invocation.getter(#issues),
- returnValue: _FakeIssuesService_95(
- this,
- Invocation.getter(#issues),
- ),
- ) as _i13.IssuesService);
- @override
- _i13.MiscService get misc => (super.noSuchMethod(
- Invocation.getter(#misc),
- returnValue: _FakeMiscService_96(
- this,
- Invocation.getter(#misc),
- ),
- ) as _i13.MiscService);
- @override
- _i13.OrganizationsService get organizations => (super.noSuchMethod(
- Invocation.getter(#organizations),
- returnValue: _FakeOrganizationsService_97(
- this,
- Invocation.getter(#organizations),
- ),
- ) as _i13.OrganizationsService);
- @override
- _i13.PullRequestsService get pullRequests => (super.noSuchMethod(
- Invocation.getter(#pullRequests),
- returnValue: _FakePullRequestsService_98(
- this,
- Invocation.getter(#pullRequests),
- ),
- ) as _i13.PullRequestsService);
- @override
- _i13.RepositoriesService get repositories => (super.noSuchMethod(
- Invocation.getter(#repositories),
- returnValue: _FakeRepositoriesService_99(
- this,
- Invocation.getter(#repositories),
- ),
- ) as _i13.RepositoriesService);
- @override
- _i13.SearchService get search => (super.noSuchMethod(
- Invocation.getter(#search),
- returnValue: _FakeSearchService_100(
- this,
- Invocation.getter(#search),
- ),
- ) as _i13.SearchService);
- @override
- _i13.UrlShortenerService get urlShortener => (super.noSuchMethod(
- Invocation.getter(#urlShortener),
- returnValue: _FakeUrlShortenerService_101(
- this,
- Invocation.getter(#urlShortener),
- ),
- ) as _i13.UrlShortenerService);
- @override
- _i13.UsersService get users => (super.noSuchMethod(
- Invocation.getter(#users),
- returnValue: _FakeUsersService_102(
- this,
- Invocation.getter(#users),
- ),
- ) as _i13.UsersService);
- @override
- _i13.ChecksService get checks => (super.noSuchMethod(
- Invocation.getter(#checks),
- returnValue: _FakeChecksService_103(
- this,
- Invocation.getter(#checks),
- ),
- ) as _i13.ChecksService);
- @override
- _i19.Future<T> getJSON<S, T>(
- String? path, {
- int? statusCode,
- void Function(_i2.Response)? fail,
- Map<String, String>? headers,
- Map<String, String>? params,
- _i13.JSONConverter<S, T>? convert,
- String? preview,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #getJSON,
- [path],
- {
- #statusCode: statusCode,
- #fail: fail,
- #headers: headers,
- #params: params,
- #convert: convert,
- #preview: preview,
- },
- ),
- returnValue: _i27.ifNotNull(
- _i27.dummyValueOrNull<T>(
- this,
- Invocation.method(
- #getJSON,
- [path],
- {
- #statusCode: statusCode,
- #fail: fail,
- #headers: headers,
- #params: params,
- #convert: convert,
- #preview: preview,
- },
- ),
- ),
- (T v) => _i19.Future<T>.value(v),
- ) ??
- _FakeFuture_22<T>(
- this,
- Invocation.method(
- #getJSON,
- [path],
- {
- #statusCode: statusCode,
- #fail: fail,
- #headers: headers,
- #params: params,
- #convert: convert,
- #preview: preview,
- },
- ),
- ),
- ) as _i19.Future<T>);
- @override
- _i19.Future<T> postJSON<S, T>(
- String? path, {
- int? statusCode,
- void Function(_i2.Response)? fail,
- Map<String, String>? headers,
- Map<String, dynamic>? params,
- _i13.JSONConverter<S, T>? convert,
- dynamic body,
- String? preview,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #postJSON,
- [path],
- {
- #statusCode: statusCode,
- #fail: fail,
- #headers: headers,
- #params: params,
- #convert: convert,
- #body: body,
- #preview: preview,
- },
- ),
- returnValue: _i40.postJsonShim<S, T>(
- path,
- statusCode: statusCode,
- fail: fail,
- headers: headers,
- params: params,
- convert: convert,
- body: body,
- preview: preview,
- ),
- ) as _i19.Future<T>);
- @override
- _i19.Future<T> putJSON<S, T>(
- String? path, {
- int? statusCode,
- void Function(_i2.Response)? fail,
- Map<String, String>? headers,
- Map<String, dynamic>? params,
- _i13.JSONConverter<S, T>? convert,
- dynamic body,
- String? preview,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #putJSON,
- [path],
- {
- #statusCode: statusCode,
- #fail: fail,
- #headers: headers,
- #params: params,
- #convert: convert,
- #body: body,
- #preview: preview,
- },
- ),
- returnValue: _i27.ifNotNull(
- _i27.dummyValueOrNull<T>(
- this,
- Invocation.method(
- #putJSON,
- [path],
- {
- #statusCode: statusCode,
- #fail: fail,
- #headers: headers,
- #params: params,
- #convert: convert,
- #body: body,
- #preview: preview,
- },
- ),
- ),
- (T v) => _i19.Future<T>.value(v),
- ) ??
- _FakeFuture_22<T>(
- this,
- Invocation.method(
- #putJSON,
- [path],
- {
- #statusCode: statusCode,
- #fail: fail,
- #headers: headers,
- #params: params,
- #convert: convert,
- #body: body,
- #preview: preview,
- },
- ),
- ),
- ) as _i19.Future<T>);
- @override
- _i19.Future<T> patchJSON<S, T>(
- String? path, {
- int? statusCode,
- void Function(_i2.Response)? fail,
- Map<String, String>? headers,
- Map<String, dynamic>? params,
- _i13.JSONConverter<S, T>? convert,
- dynamic body,
- String? preview,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #patchJSON,
- [path],
- {
- #statusCode: statusCode,
- #fail: fail,
- #headers: headers,
- #params: params,
- #convert: convert,
- #body: body,
- #preview: preview,
- },
- ),
- returnValue: _i27.ifNotNull(
- _i27.dummyValueOrNull<T>(
- this,
- Invocation.method(
- #patchJSON,
- [path],
- {
- #statusCode: statusCode,
- #fail: fail,
- #headers: headers,
- #params: params,
- #convert: convert,
- #body: body,
- #preview: preview,
- },
- ),
- ),
- (T v) => _i19.Future<T>.value(v),
- ) ??
- _FakeFuture_22<T>(
- this,
- Invocation.method(
- #patchJSON,
- [path],
- {
- #statusCode: statusCode,
- #fail: fail,
- #headers: headers,
- #params: params,
- #convert: convert,
- #body: body,
- #preview: preview,
- },
- ),
- ),
- ) as _i19.Future<T>);
- @override
- _i19.Future<T> requestJson<S, T>(
- String? method,
- String? path, {
- int? statusCode,
- void Function(_i2.Response)? fail,
- Map<String, String>? headers,
- Map<String, dynamic>? params,
- _i13.JSONConverter<S, T?>? convert,
- dynamic body,
- String? preview,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #requestJson,
- [
- method,
- path,
- ],
- {
- #statusCode: statusCode,
- #fail: fail,
- #headers: headers,
- #params: params,
- #convert: convert,
- #body: body,
- #preview: preview,
- },
- ),
- returnValue: _i27.ifNotNull(
- _i27.dummyValueOrNull<T>(
- this,
- Invocation.method(
- #requestJson,
- [
- method,
- path,
- ],
- {
- #statusCode: statusCode,
- #fail: fail,
- #headers: headers,
- #params: params,
- #convert: convert,
- #body: body,
- #preview: preview,
- },
- ),
- ),
- (T v) => _i19.Future<T>.value(v),
- ) ??
- _FakeFuture_22<T>(
- this,
- Invocation.method(
- #requestJson,
- [
- method,
- path,
- ],
- {
- #statusCode: statusCode,
- #fail: fail,
- #headers: headers,
- #params: params,
- #convert: convert,
- #body: body,
- #preview: preview,
- },
- ),
- ),
- ) as _i19.Future<T>);
- @override
- _i19.Future<_i2.Response> request(
- String? method,
- String? path, {
- Map<String, String>? headers,
- Map<String, dynamic>? params,
- dynamic body,
- int? statusCode,
- void Function(_i2.Response)? fail,
- String? preview,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #request,
- [
- method,
- path,
- ],
- {
- #headers: headers,
- #params: params,
- #body: body,
- #statusCode: statusCode,
- #fail: fail,
- #preview: preview,
- },
- ),
- returnValue: _i19.Future<_i2.Response>.value(_FakeResponse_104(
- this,
- Invocation.method(
- #request,
- [
- method,
- path,
- ],
- {
- #headers: headers,
- #params: params,
- #body: body,
- #statusCode: statusCode,
- #fail: fail,
- #preview: preview,
- },
- ),
- )),
- ) as _i19.Future<_i2.Response>);
- @override
- Never handleStatusCode(_i2.Response? response) => (super.noSuchMethod(
- Invocation.method(
- #handleStatusCode,
- [response],
- ),
- returnValue: null,
- ) as Never);
- @override
- void dispose() => super.noSuchMethod(
- Invocation.method(
- #dispose,
- [],
- ),
- returnValueForMissingStub: null,
- );
-}
diff --git a/app_dart/test/src/utilities/push_message.dart b/app_dart/test/src/utilities/push_message.dart
deleted file mode 100644
index d3f2236..0000000
--- a/app_dart/test/src/utilities/push_message.dart
+++ /dev/null
@@ -1,197 +0,0 @@
-// Copyright 2020 The Flutter 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:convert';
-
-import 'package:cocoon_service/src/model/luci/push_message.dart';
-
-const String ref = 'deadbeef';
-
-PushMessage createBuildbucketPushMessage(
- String status, {
- String? result,
- String builderName = 'Linux Coverage',
- String urlParam = '',
- int retries = 0,
- String? failureReason,
- String? userData = '',
-}) {
- return PushMessage(
- data: buildPushMessageJson(
- status,
- result: result,
- builderName: builderName,
- urlParam: urlParam,
- retries: retries,
- failureReason: failureReason,
- userData: userData,
- ),
- messageId: '123',
- );
-}
-
-PushMessage pushMessageJsonNoBuildset(
- String status, {
- String? result,
- String builderName = 'Linux Coverage',
- String urlParam = '',
- int retries = 0,
- String? failureReason,
-}) {
- return PushMessage(
- data: buildPushMessageJsonNoBuildset(
- status,
- result: result,
- builderName: builderName,
- urlParam: urlParam,
- retries: retries,
- failureReason: failureReason,
- ),
- messageId: '123',
- );
-}
-
-String buildPushMessageJson(
- String status, {
- String? result,
- String builderName = 'Linux Coverage',
- String urlParam = '',
- int retries = 0,
- String? failureReason,
- String? userData,
-}) =>
- base64.encode(
- utf8.encode(
- buildPushMessageString(
- status,
- result: result,
- builderName: builderName,
- urlParam: urlParam,
- retries: retries,
- failureReason: failureReason,
- userData: userData,
- ),
- ),
- );
-
-String buildPushMessageJsonNoBuildset(
- String status, {
- String? result,
- String builderName = 'Linux Coverage',
- String urlParam = '',
- int retries = 0,
- String? failureReason,
-}) =>
- base64.encode(
- utf8.encode(
- buildPushMessageNoBuildsetString(
- status,
- result: result,
- builderName: builderName,
- urlParam: urlParam,
- retries: retries,
- failureReason: failureReason,
- ),
- ),
- );
-
-String buildPushMessageString(
- String status, {
- String? result,
- String builderName = 'Linux Coverage',
- String urlParam = '',
- int retries = 0,
- String? failureReason,
- String? userData,
-}) {
- return '''{
- "build": {
- "bucket": "luci.flutter.prod",
- "canary": false,
- "canary_preference": "PROD",
- "created_by": "user:dnfield@google.com",
- "created_ts": "1565049186247524",
- "completed_ts": "1565049193786090",
- "experimental": false,
- ${failureReason != null ? '"failure_reason": "$failureReason",' : ''}
- "id": "8905920700440101120",
- "parameters_json": "{\\"builder_name\\": \\"$builderName\\", \\"properties\\": {\\"git_ref\\": \\"refs/pull/37647/head\\", \\"git_url\\": \\"https://github.com/flutter/flutter\\", \\"cores\\": [8]}}",
- "project": "flutter",
- ${result != null ? '"result": "$result",' : ''}
- "result_details_json": "{\\"properties\\": {}, \\"swarming\\": {\\"bot_dimensions\\": {\\"caches\\": [\\"flutter_openjdk_install\\", \\"git\\", \\"goma_v2\\", \\"vpython\\"], \\"cores\\": [\\"8\\"], \\"cpu\\": [\\"x86\\", \\"x86-64\\", \\"x86-64-Broadwell_GCE\\", \\"x86-64-avx2\\"], \\"gce\\": [\\"1\\"], \\"gpu\\": [\\"none\\"], \\"id\\": [\\"luci-flutter-prod-xenial-2-bnrz\\"], \\"image\\": [\\"chrome-xenial-19052201-9cb74617499\\"], \\"inside_docker\\": [\\"0\\"], \\"kvm\\": [\\"1\\"], \\"locale\\": [\\"en_US.UTF-8\\"], \\"machine_type\\": [\\"n1-standard-8\\"], \\"os\\": [\\"Linux\\", \\"Ubuntu\\", \\"Ubuntu-16.04\\"], \\"pool\\": [\\"luci.flutter.prod\\"], \\"python\\": [\\"2.7.12\\"], \\"server_version\\": [\\"4382-5929880\\"], \\"ssd\\": [\\"0\\"], \\"zone\\": [\\"us\\", \\"us-central\\", \\"us-central1\\", \\"us-central1-c\\"]}}}",
- "service_account": "flutter-prod-builder@chops-service-accounts.iam.gserviceaccount.com",
- "started_ts": "1565049193786080",
- "status": "$status",
- "status_changed_ts": "1565049194386647",
- "tags": [
- "build_address:luci.flutter.prod/$builderName/1698",
- "builder:$builderName",
- "buildset:pr/git/37647",
- "buildset:sha/git/$ref",
- "github_link:https://github.com/flutter/flutter/pull/37647",
- "swarming_hostname:chromium-swarm.appspot.com",
- "swarming_tag:log_location:logdog://logs.chromium.org/flutter/buildbucket/cr-buildbucket.appspot.com/8905920700440101120/+/annotations",
- "swarming_tag:luci_project:flutter",
- "swarming_tag:os:Linux",
- "swarming_tag:recipe_name:flutter/flutter",
- "swarming_tag:recipe_package:infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",
- "swarming_task_id:467d04f2f022d510",
- "user_agent:flutter-cocoon"
- ],
- "updated_ts": "1565049194391321",
- "url": "https://ci.chromium.org/b/8905920700440101120$urlParam",
- "utcnow_ts": "1565049194653640"
- },
- ${userData != null ? '"user_data": "$userData",' : ''}
- "hostname": "cr-buildbucket.appspot.com"
-}''';
-}
-
-String buildPushMessageNoBuildsetString(
- String status, {
- String? result,
- String builderName = 'Linux Coverage',
- String urlParam = '',
- int retries = 0,
- String? failureReason,
-}) {
- return '''{
- "build": {
- "bucket": "luci.flutter.prod",
- "canary": false,
- "canary_preference": "PROD",
- "created_by": "user:dnfield@google.com",
- "created_ts": "1565049186247524",
- "experimental": false,
- ${failureReason != null ? '"failure_reason": "$failureReason",' : ''}
- "id": "8905920700440101120",
- "parameters_json": "{\\"builder_name\\": \\"$builderName\\", \\"properties\\": {\\"git_ref\\": \\"refs/pull/37647/head\\", \\"git_url\\": \\"https://github.com/flutter/flutter\\"}}",
- "project": "flutter",
- ${result != null ? '"result": "$result",' : ''}
- "result_details_json": "{\\"properties\\": {}, \\"swarming\\": {\\"bot_dimensions\\": {\\"caches\\": [\\"flutter_openjdk_install\\", \\"git\\", \\"goma_v2\\", \\"vpython\\"], \\"cores\\": [\\"8\\"], \\"cpu\\": [\\"x86\\", \\"x86-64\\", \\"x86-64-Broadwell_GCE\\", \\"x86-64-avx2\\"], \\"gce\\": [\\"1\\"], \\"gpu\\": [\\"none\\"], \\"id\\": [\\"luci-flutter-prod-xenial-2-bnrz\\"], \\"image\\": [\\"chrome-xenial-19052201-9cb74617499\\"], \\"inside_docker\\": [\\"0\\"], \\"kvm\\": [\\"1\\"], \\"locale\\": [\\"en_US.UTF-8\\"], \\"machine_type\\": [\\"n1-standard-8\\"], \\"os\\": [\\"Linux\\", \\"Ubuntu\\", \\"Ubuntu-16.04\\"], \\"pool\\": [\\"luci.flutter.prod\\"], \\"python\\": [\\"2.7.12\\"], \\"server_version\\": [\\"4382-5929880\\"], \\"ssd\\": [\\"0\\"], \\"zone\\": [\\"us\\", \\"us-central\\", \\"us-central1\\", \\"us-central1-c\\"]}}}",
- "service_account": "flutter-prod-builder@chops-service-accounts.iam.gserviceaccount.com",
- "started_ts": "1565049193786080",
- "status": "$status",
- "status_changed_ts": "1565049194386647",
- "tags": [
- "build_address:luci.flutter.prod/$builderName/1698",
- "builder:$builderName",
- "github_link:https://github.com/flutter/flutter/pull/37647",
- "swarming_hostname:chromium-swarm.appspot.com",
- "swarming_tag:log_location:logdog://logs.chromium.org/flutter/buildbucket/cr-buildbucket.appspot.com/8905920700440101120/+/annotations",
- "swarming_tag:luci_project:flutter",
- "swarming_tag:os:Linux",
- "swarming_tag:recipe_name:flutter/flutter",
- "swarming_tag:recipe_package:infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",
- "swarming_task_id:467d04f2f022d510",
- "user_agent:flutter-cocoon"
- ],
- "updated_ts": "1565049194391321",
- "url": "https://ci.chromium.org/b/8905920700440101120$urlParam",
- "utcnow_ts": "1565049194653640"
- },
- "hostname": "cr-buildbucket.appspot.com",
- "user_data": "{\\"retries\\": $retries}"
-}''';
-}
diff --git a/app_dart/test/src/utilities/webhook_generators.dart b/app_dart/test/src/utilities/webhook_generators.dart
deleted file mode 100644
index aa9c9a2..0000000
--- a/app_dart/test/src/utilities/webhook_generators.dart
+++ /dev/null
@@ -1,1072 +0,0 @@
-// Copyright 2019 The Flutter 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:convert';
-
-import 'package:cocoon_service/protos.dart' as pb;
-import 'package:cocoon_service/src/model/luci/push_message.dart';
-import 'package:cocoon_service/src/service/config.dart';
-import 'package:github/github.dart';
-import 'package:github/hooks.dart';
-
-PushMessage generateGithubWebhookMessage({
- String event = 'pull_request',
- String action = 'merged',
- int number = 123,
- String? baseRef,
- String baseSha = '4cd12fc8b7d4cc2d8609182e1c4dea5cddc86890',
- String login = 'dash',
- String headRef = 'abc',
- bool isDraft = false,
- bool merged = false,
- bool mergeable = true,
- String mergeCommitSha = 'fd6b46416c18de36ce87d0241994b2da180cab4c',
- RepositorySlug? slug,
-}) {
- final String data = (pb.GithubWebhookMessage.create()
- ..event = event
- ..payload = _generatePullRequestEvent(
- action,
- number,
- baseRef,
- baseSha: baseSha,
- login: login,
- headRef: headRef,
- isDraft: isDraft,
- merged: merged,
- isMergeable: mergeable,
- slug: slug,
- mergeCommitSha: mergeCommitSha,
- ))
- .writeToJson();
- return PushMessage(data: data, messageId: 'abc123');
-}
-
-String _generatePullRequestEvent(
- String action,
- int number,
- String? baseRef, {
- RepositorySlug? slug,
- String login = 'flutter',
- String baseSha = '4cd12fc8b7d4cc2d8609182e1c4dea5cddc86890',
- String headRef = 'wait_for_reassemble',
- bool includeCqLabel = false,
- bool isDraft = false,
- bool merged = false,
- bool isMergeable = true,
- String mergeCommitSha = 'fd6b46416c18de36ce87d0241994b2da180cab4c',
-}) {
- slug ??= Config.flutterSlug;
- baseRef ??= Config.defaultBranch(slug);
- return '''{
- "action": "$action",
- "number": $number,
- "pull_request": {
- "url": "https://api.github.com/repos/${slug.fullName}/pulls/$number",
- "id": 294034,
- "node_id": "MDExOlB1bGxSZXF1ZXN0Mjk0MDMzODQx",
- "html_url": "https://github.com/${slug.fullName}/pull/$number",
- "diff_url": "https://github.com/${slug.fullName}/pull/$number.diff",
- "patch_url": "https://github.com/${slug.fullName}/pull/$number.patch",
- "issue_url": "https://api.github.com/repos/${slug.fullName}/issues/$number",
- "number": $number,
- "state": "open",
- "locked": false,
- "title": "Defer reassemble until reload is finished",
- "user": {
- "login": "$login",
- "id": 862741,
- "node_id": "MDQ6VXNlcjg2MjA3NDE=",
- "avatar_url": "https://avatars3.githubusercontent.com/u/8620741?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/flutter",
- "html_url": "https://github.com/flutter",
- "followers_url": "https://api.github.com/users/flutter/followers",
- "following_url": "https://api.github.com/users/flutter/following{/other_user}",
- "gists_url": "https://api.github.com/users/flutter/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/flutter/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/flutter/subscriptions",
- "organizations_url": "https://api.github.com/users/flutter/orgs",
- "repos_url": "https://api.github.com/users/flutter/repos",
- "events_url": "https://api.github.com/users/flutter/events{/privacy}",
- "received_events_url": "https://api.github.com/users/flutter/received_events",
- "type": "User",
- "site_admin": false
- },
- "draft" : "$isDraft",
- "body": "The body",
- "created_at": "2019-07-03T07:14:35Z",
- "updated_at": "2019-07-03T16:34:53Z",
- "closed_at": null,
- "merged_at": "2019-07-03T16:34:53Z",
- "merge_commit_sha": "$mergeCommitSha",
- "assignee": null,
- "assignees": [],
- "requested_reviewers": [],
- "requested_teams": [],
- "labels": [
- {
- "id": 487496476,
- "node_id": "MDU6TGFiZWw0ODc0OTY0NzY=",
- "url": "https://api.github.com/repos/${slug.fullName}/labels/cla:%20yes",
- "name": "cla: yes",
- "color": "ffffff",
- "default": false
- },
- {
- "id": 284437560,
- "node_id": "MDU6TGFiZWwyODQ0Mzc1NjA=",
- "url": "https://api.github.com/repos/${slug.fullName}/labels/framework",
- "name": "framework",
- "color": "207de5",
- "default": false
- },
- ${includeCqLabel ? '''
- {
- "id": 283480100,
- "node_id": "MDU6TGFiZWwyODM0ODAxMDA=",
- "url": "https://api.github.com/repos/${slug.fullName}/labels/tool",
- "color": "5319e7",
- "default": false
- },''' : ''}
- {
- "id": 283480100,
- "node_id": "MDU6TGFiZWwyODM0ODAxMDA=",
- "url": "https://api.github.com/repos/${slug.fullName}/labels/tool",
- "name": "tool",
- "color": "5319e7",
- "default": false
- }
- ],
- "milestone": null,
- "commits_url": "https://api.github.com/repos/${slug.fullName}/pulls/$number/commits",
- "review_comments_url": "https://api.github.com/repos/${slug.fullName}/pulls/$number/comments",
- "review_comment_url": "https://api.github.com/repos/${slug.fullName}/pulls/comments{/number}",
- "comments_url": "https://api.github.com/repos/${slug.fullName}/issues/$number/comments",
- "statuses_url": "https://api.github.com/repos/${slug.fullName}/statuses/be6ff099a4ee56e152a5fa2f37edd10f79d1269a",
- "head": {
- "label": "$login:$headRef",
- "ref": "$headRef",
- "sha": "be6ff099a4ee56e152a5fa2f37edd10f79d1269a",
- "user": {
- "login": "$login",
- "id": 8620741,
- "node_id": "MDQ6VXNlcjg2MjA3NDE=",
- "avatar_url": "https://avatars3.githubusercontent.com/u/8620741?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/flutter",
- "html_url": "https://github.com/flutter",
- "followers_url": "https://api.github.com/users/flutter/followers",
- "following_url": "https://api.github.com/users/flutter/following{/other_user}",
- "gists_url": "https://api.github.com/users/flutter/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/flutter/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/flutter/subscriptions",
- "organizations_url": "https://api.github.com/users/flutter/orgs",
- "repos_url": "https://api.github.com/users/flutter/repos",
- "events_url": "https://api.github.com/users/flutter/events{/privacy}",
- "received_events_url": "https://api.github.com/users/flutter/received_events",
- "type": "User",
- "site_admin": false
- },
- "repo": {
- "id": 131232406,
- "node_id": "MDEwOlJlcG9zaXRvcnkxMzEyMzI0MDY=",
- "name": "${slug.name}",
- "full_name": "${slug.fullName}",
- "private": false,
- "owner": {
- "login": "flutter",
- "id": 8620741,
- "node_id": "MDQ6VXNlcjg2MjA3NDE=",
- "avatar_url": "https://avatars3.githubusercontent.com/u/8620741?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/flutter",
- "html_url": "https://github.com/flutter",
- "followers_url": "https://api.github.com/users/flutter/followers",
- "following_url": "https://api.github.com/users/flutter/following{/other_user}",
- "gists_url": "https://api.github.com/users/flutter/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/flutter/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/flutter/subscriptions",
- "organizations_url": "https://api.github.com/users/flutter/orgs",
- "repos_url": "https://api.github.com/users/flutter/repos",
- "events_url": "https://api.github.com/users/flutter/events{/privacy}",
- "received_events_url": "https://api.github.com/users/flutter/received_events",
- "type": "User",
- "site_admin": false
- },
- "html_url": "https://github.com/${slug.fullName}",
- "description": "Flutter makes it easy and fast to build beautiful mobile apps.",
- "fork": true,
- "url": "https://api.github.com/repos/${slug.fullName}",
- "forks_url": "https://api.github.com/repos/${slug.fullName}/forks",
- "keys_url": "https://api.github.com/repos/${slug.fullName}/keys{/key_id}",
- "collaborators_url": "https://api.github.com/repos/${slug.fullName}/collaborators{/collaborator}",
- "teams_url": "https://api.github.com/repos/${slug.fullName}/teams",
- "hooks_url": "https://api.github.com/repos/${slug.fullName}/hooks",
- "issue_events_url": "https://api.github.com/repos/${slug.fullName}/issues/events{/number}",
- "events_url": "https://api.github.com/repos/${slug.fullName}/events",
- "assignees_url": "https://api.github.com/repos/${slug.fullName}/assignees{/user}",
- "branches_url": "https://api.github.com/repos/${slug.fullName}/branches{/branch}",
- "tags_url": "https://api.github.com/repos/${slug.fullName}/tags",
- "blobs_url": "https://api.github.com/repos/${slug.fullName}/git/blobs{/sha}",
- "git_tags_url": "https://api.github.com/repos/${slug.fullName}/git/tags{/sha}",
- "git_refs_url": "https://api.github.com/repos/${slug.fullName}/git/refs{/sha}",
- "trees_url": "https://api.github.com/repos/${slug.fullName}/git/trees{/sha}",
- "statuses_url": "https://api.github.com/repos/${slug.fullName}/statuses/{sha}",
- "languages_url": "https://api.github.com/repos/${slug.fullName}/languages",
- "stargazers_url": "https://api.github.com/repos/${slug.fullName}/stargazers",
- "contributors_url": "https://api.github.com/repos/${slug.fullName}/contributors",
- "subscribers_url": "https://api.github.com/repos/${slug.fullName}/subscribers",
- "subscription_url": "https://api.github.com/repos/${slug.fullName}/subscription",
- "commits_url": "https://api.github.com/repos/${slug.fullName}/commits{/sha}",
- "git_commits_url": "https://api.github.com/repos/${slug.fullName}/git/commits{/sha}",
- "comments_url": "https://api.github.com/repos/${slug.fullName}/comments{/number}",
- "issue_comment_url": "https://api.github.com/repos/${slug.fullName}/issues/comments{/number}",
- "contents_url": "https://api.github.com/repos/${slug.fullName}/contents/{+path}",
- "compare_url": "https://api.github.com/repos/${slug.fullName}/compare/{base}...{head}",
- "merges_url": "https://api.github.com/repos/${slug.fullName}/merges",
- "archive_url": "https://api.github.com/repos/${slug.fullName}/{archive_format}{/ref}",
- "downloads_url": "https://api.github.com/repos/${slug.fullName}/downloads",
- "issues_url": "https://api.github.com/repos/${slug.fullName}/issues{/number}",
- "pulls_url": "https://api.github.com/repos/${slug.fullName}/pulls{/number}",
- "milestones_url": "https://api.github.com/repos/${slug.fullName}/milestones{/number}",
- "notifications_url": "https://api.github.com/repos/${slug.fullName}/notifications{?since,all,participating}",
- "labels_url": "https://api.github.com/repos/${slug.fullName}/labels{/name}",
- "releases_url": "https://api.github.com/repos/${slug.fullName}/releases{/id}",
- "deployments_url": "https://api.github.com/repos/${slug.fullName}/deployments",
- "created_at": "2018-04-27T02:03:08Z",
- "updated_at": "2019-06-27T06:56:59Z",
- "pushed_at": "2019-07-03T19:40:11Z",
- "git_url": "git://github.com/${slug.fullName}.git",
- "ssh_url": "git@github.com:${slug.fullName}.git",
- "clone_url": "https://github.com/${slug.fullName}.git",
- "svn_url": "https://github.com/${slug.fullName}",
- "homepage": "https://flutter.io",
- "size": 94508,
- "stargazers_count": 1,
- "watchers_count": 1,
- "language": "Dart",
- "has_issues": false,
- "has_projects": true,
- "has_downloads": true,
- "has_wiki": true,
- "has_pages": false,
- "forks_count": 0,
- "mirror_url": null,
- "archived": false,
- "disabled": false,
- "open_issues_count": 0,
- "license": {
- "key": "other",
- "name": "Other",
- "spdx_id": "NOASSERTION",
- "url": null,
- "node_id": "MDc6TGljZW5zZTA="
- },
- "forks": 0,
- "open_issues": 0,
- "watchers": 1,
- "default_branch": "$kDefaultBranchName"
- }
- },
- "base": {
- "label": "flutter:$baseRef",
- "ref": "$baseRef",
- "sha": "$baseSha",
- "user": {
- "login": "flutter",
- "id": 14101776,
- "node_id": "MDEyOk9yZ2FuaXphdGlvbjE0MTAxNzc2",
- "avatar_url": "https://avatars3.githubblahblahblah",
- "gravatar_id": "",
- "url": "https://api.github.com/users/flutter",
- "html_url": "https://github.com/flutter",
- "followers_url": "https://api.github.com/users/flutter/followers",
- "following_url": "https://api.github.com/users/flutter/following{/other_user}",
- "gists_url": "https://api.github.com/users/flutter/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/flutter/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/flutter/subscriptions",
- "organizations_url": "https://api.github.com/users/flutter/orgs",
- "repos_url": "https://api.github.com/users/flutter/repos",
- "events_url": "https://api.github.com/users/flutter/events{/privacy}",
- "received_events_url": "https://api.github.com/users/flutter/received_events",
- "type": "Organization",
- "site_admin": false
- },
- "repo": {
- "id": 31792824,
- "node_id": "MDEwOlJlcG9zaXRvcnkzMTc5MjgyNA==",
- "name": "${slug.name}",
- "full_name": "${slug.fullName}",
- "private": false,
- "owner": {
- "login": "flutter",
- "id": 14101776,
- "node_id": "MDEyOk9yZ2FuaXphdGlvbjE0MTAxNzc2",
- "avatar_url": "https://avatars3.githubblahblahblah",
- "gravatar_id": "",
- "url": "https://api.github.com/users/flutter",
- "html_url": "https://github.com/flutter",
- "followers_url": "https://api.github.com/users/flutter/followers",
- "following_url": "https://api.github.com/users/flutter/following{/other_user}",
- "gists_url": "https://api.github.com/users/flutter/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/flutter/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/flutter/subscriptions",
- "organizations_url": "https://api.github.com/users/flutter/orgs",
- "repos_url": "https://api.github.com/users/flutter/repos",
- "events_url": "https://api.github.com/users/flutter/events{/privacy}",
- "received_events_url": "https://api.github.com/users/flutter/received_events",
- "type": "Organization",
- "site_admin": false
- },
- "html_url": "https://github.com/${slug.fullName}",
- "description": "Flutter makes it easy and fast to build beautiful mobile apps.",
- "fork": false,
- "url": "https://api.github.com/repos/${slug.fullName}",
- "forks_url": "https://api.github.com/repos/${slug.fullName}/forks",
- "keys_url": "https://api.github.com/repos/${slug.fullName}/keys{/key_id}",
- "collaborators_url": "https://api.github.com/repos/${slug.fullName}/collaborators{/collaborator}",
- "teams_url": "https://api.github.com/repos/${slug.fullName}/teams",
- "hooks_url": "https://api.github.com/repos/${slug.fullName}/hooks",
- "issue_events_url": "https://api.github.com/repos/${slug.fullName}/issues/events{/number}",
- "events_url": "https://api.github.com/repos/${slug.fullName}/events",
- "assignees_url": "https://api.github.com/repos/${slug.fullName}/assignees{/user}",
- "branches_url": "https://api.github.com/repos/${slug.fullName}/branches{/branch}",
- "tags_url": "https://api.github.com/repos/${slug.fullName}/tags",
- "blobs_url": "https://api.github.com/repos/${slug.fullName}/git/blobs{/sha}",
- "git_tags_url": "https://api.github.com/repos/${slug.fullName}/git/tags{/sha}",
- "git_refs_url": "https://api.github.com/repos/${slug.fullName}/git/refs{/sha}",
- "trees_url": "https://api.github.com/repos/${slug.fullName}/git/trees{/sha}",
- "statuses_url": "https://api.github.com/repos/${slug.fullName}/statuses/{sha}",
- "languages_url": "https://api.github.com/repos/${slug.fullName}/languages",
- "stargazers_url": "https://api.github.com/repos/${slug.fullName}/stargazers",
- "contributors_url": "https://api.github.com/repos/${slug.fullName}/contributors",
- "subscribers_url": "https://api.github.com/repos/${slug.fullName}/subscribers",
- "subscription_url": "https://api.github.com/repos/${slug.fullName}/subscription",
- "commits_url": "https://api.github.com/repos/${slug.fullName}/commits{/sha}",
- "git_commits_url": "https://api.github.com/repos/${slug.fullName}/git/commits{/sha}",
- "comments_url": "https://api.github.com/repos/${slug.fullName}/comments{/number}",
- "issue_comment_url": "https://api.github.com/repos/${slug.fullName}/issues/comments{/number}",
- "contents_url": "https://api.github.com/repos/${slug.fullName}/contents/{+path}",
- "compare_url": "https://api.github.com/repos/${slug.fullName}/compare/{base}...{head}",
- "merges_url": "https://api.github.com/repos/${slug.fullName}/merges",
- "archive_url": "https://api.github.com/repos/${slug.fullName}/{archive_format}{/ref}",
- "downloads_url": "https://api.github.com/repos/${slug.fullName}/downloads",
- "issues_url": "https://api.github.com/repos/${slug.fullName}/issues{/number}",
- "pulls_url": "https://api.github.com/repos/${slug.fullName}/pulls{/number}",
- "milestones_url": "https://api.github.com/repos/${slug.fullName}/milestones{/number}",
- "notifications_url": "https://api.github.com/repos/${slug.fullName}/notifications{?since,all,participating}",
- "labels_url": "https://api.github.com/repos/${slug.fullName}/labels{/name}",
- "releases_url": "https://api.github.com/repos/${slug.fullName}/releases{/id}",
- "deployments_url": "https://api.github.com/repos/${slug.fullName}/deployments",
- "created_at": "2015-03-06T22:54:58Z",
- "updated_at": "2019-07-04T02:08:44Z",
- "pushed_at": "2019-07-04T02:03:04Z",
- "git_url": "git://github.com/${slug.fullName}.git",
- "ssh_url": "git@github.com:${slug.fullName}.git",
- "clone_url": "https://github.com/${slug.fullName}.git",
- "svn_url": "https://github.com/${slug.fullName}",
- "homepage": "https://flutter.dev",
- "size": 65507,
- "stargazers_count": 68944,
- "watchers_count": 68944,
- "language": "Dart",
- "has_issues": true,
- "has_projects": true,
- "has_downloads": true,
- "has_wiki": true,
- "has_pages": false,
- "forks_count": 7987,
- "mirror_url": null,
- "archived": false,
- "disabled": false,
- "open_issues_count": 6536,
- "license": {
- "key": "other",
- "name": "Other",
- "spdx_id": "NOASSERTION",
- "url": null,
- "node_id": "MDc6TGljZW5zZTA="
- },
- "forks": 7987,
- "open_issues": 6536,
- "watchers": 68944,
- "default_branch": "$kDefaultBranchName"
- }
- },
- "_links": {
- "self": {
- "href": "https://api.github.com/repos/${slug.fullName}/pulls/$number"
- },
- "html": {
- "href": "https://github.com/${slug.fullName}/pull/$number"
- },
- "issue": {
- "href": "https://api.github.com/repos/${slug.fullName}/issues/$number"
- },
- "comments": {
- "href": "https://api.github.com/repos/${slug.fullName}/issues/$number/comments"
- },
- "review_comments": {
- "href": "https://api.github.com/repos/${slug.fullName}/pulls/$number/comments"
- },
- "review_comment": {
- "href": "https://api.github.com/repos/${slug.fullName}/pulls/comments{/number}"
- },
- "commits": {
- "href": "https://api.github.com/repos/${slug.fullName}/pulls/$number/commits"
- },
- "statuses": {
- "href": "https://api.github.com/repos/${slug.fullName}/statuses/deadbeef"
- }
- },
- "author_association": "MEMBER",
- "draft" : $isDraft,
- "merged": $merged,
- "mergeable": $isMergeable,
- "rebaseable": true,
- "mergeable_state": "draft",
- "merged_by": null,
- "comments": 1,
- "review_comments": 0,
- "maintainer_can_modify": true,
- "commits": 5,
- "additions": 55,
- "deletions": 36,
- "changed_files": 5
- },
- "repository": {
- "id": 1868532,
- "node_id": "MDEwOlJlcG9zaXRvcnkxODY4NTMwMDI=",
- "name": "${slug.name}",
- "full_name": "${slug.fullName}",
- "private": false,
- "owner": {
- "login": "flutter",
- "id": 21031067,
- "node_id": "MDQ6VXNlcjIxMDMxMDY3",
- "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/flutter",
- "html_url": "https://github.com/flutter",
- "followers_url": "https://api.github.com/users/flutter/followers",
- "following_url": "https://api.github.com/users/flutter/following{/other_user}",
- "gists_url": "https://api.github.com/users/flutter/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/flutter/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/flutter/subscriptions",
- "organizations_url": "https://api.github.com/users/flutter/orgs",
- "repos_url": "https://api.github.com/users/flutter/repos",
- "events_url": "https://api.github.com/users/flutter/events{/privacy}",
- "received_events_url": "https://api.github.com/users/flutter/received_events",
- "type": "User",
- "site_admin": false
- },
- "html_url": "https://github.com/${slug.fullName}",
- "description": null,
- "fork": false,
- "url": "https://api.github.com/repos/${slug.fullName}",
- "forks_url": "https://api.github.com/repos/${slug.fullName}/forks",
- "keys_url": "https://api.github.com/repos/${slug.fullName}/keys{/key_id}",
- "collaborators_url": "https://api.github.com/repos/${slug.fullName}/collaborators{/collaborator}",
- "teams_url": "https://api.github.com/repos/${slug.fullName}/teams",
- "hooks_url": "https://api.github.com/repos/${slug.fullName}/hooks",
- "issue_events_url": "https://api.github.com/repos/${slug.fullName}/issues/events{/number}",
- "events_url": "https://api.github.com/repos/${slug.fullName}/events",
- "assignees_url": "https://api.github.com/repos/${slug.fullName}/assignees{/user}",
- "branches_url": "https://api.github.com/repos/${slug.fullName}/branches{/branch}",
- "tags_url": "https://api.github.com/repos/${slug.fullName}/tags",
- "blobs_url": "https://api.github.com/repos/${slug.fullName}/git/blobs{/sha}",
- "git_tags_url": "https://api.github.com/repos/${slug.fullName}/git/tags{/sha}",
- "git_refs_url": "https://api.github.com/repos/${slug.fullName}/git/refs{/sha}",
- "trees_url": "https://api.github.com/repos/${slug.fullName}/git/trees{/sha}",
- "statuses_url": "https://api.github.com/repos/${slug.fullName}/statuses/{sha}",
- "languages_url": "https://api.github.com/repos/${slug.fullName}/languages",
- "stargazers_url": "https://api.github.com/repos/${slug.fullName}/stargazers",
- "contributors_url": "https://api.github.com/repos/${slug.fullName}/contributors",
- "subscribers_url": "https://api.github.com/repos/${slug.fullName}/subscribers",
- "subscription_url": "https://api.github.com/repos/${slug.fullName}/subscription",
- "commits_url": "https://api.github.com/repos/${slug.fullName}/commits{/sha}",
- "git_commits_url": "https://api.github.com/repos/${slug.fullName}/git/commits{/sha}",
- "comments_url": "https://api.github.com/repos/${slug.fullName}/comments{/number}",
- "issue_comment_url": "https://api.github.com/repos/${slug.fullName}/issues/comments{/number}",
- "contents_url": "https://api.github.com/repos/${slug.fullName}/contents/{+path}",
- "compare_url": "https://api.github.com/repos/${slug.fullName}/compare/{base}...{head}",
- "merges_url": "https://api.github.com/repos/${slug.fullName}/merges",
- "archive_url": "https://api.github.com/repos/${slug.fullName}/{archive_format}{/ref}",
- "downloads_url": "https://api.github.com/repos/${slug.fullName}/downloads",
- "issues_url": "https://api.github.com/repos/${slug.fullName}/issues{/number}",
- "pulls_url": "https://api.github.com/repos/${slug.fullName}/pulls{/number}",
- "milestones_url": "https://api.github.com/repos/${slug.fullName}/milestones{/number}",
- "notifications_url": "https://api.github.com/repos/${slug.fullName}/notifications{?since,all,participating}",
- "labels_url": "https://api.github.com/repos/${slug.fullName}/labels{/name}",
- "releases_url": "https://api.github.com/repos/${slug.fullName}/releases{/id}",
- "deployments_url": "https://api.github.com/repos/${slug.fullName}/deployments",
- "created_at": "2019-05-15T15:19:25Z",
- "updated_at": "2019-05-15T15:19:27Z",
- "pushed_at": "2019-05-15T15:20:32Z",
- "git_url": "git://github.com/${slug.fullName}.git",
- "ssh_url": "git@github.com:${slug.fullName}.git",
- "clone_url": "https://github.com/${slug.fullName}.git",
- "svn_url": "https://github.com/${slug.fullName}",
- "homepage": null,
- "size": 0,
- "stargazers_count": 0,
- "watchers_count": 0,
- "language": null,
- "has_issues": true,
- "has_projects": true,
- "has_downloads": true,
- "has_wiki": true,
- "has_pages": true,
- "forks_count": 0,
- "mirror_url": null,
- "archived": false,
- "disabled": false,
- "open_issues_count": 2,
- "license": null,
- "forks": 0,
- "open_issues": 2,
- "watchers": 0,
- "default_branch": "$kDefaultBranchName"
- },
- "sender": {
- "login": "$login",
- "id": 21031067,
- "node_id": "MDQ6VXNlcjIxMDMxMDY3",
- "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/flutter",
- "html_url": "https://github.com/flutter",
- "followers_url": "https://api.github.com/users/flutter/followers",
- "following_url": "https://api.github.com/users/flutter/following{/other_user}",
- "gists_url": "https://api.github.com/users/flutter/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/flutter/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/flutter/subscriptions",
- "organizations_url": "https://api.github.com/users/flutter/orgs",
- "repos_url": "https://api.github.com/users/flutter/repos",
- "events_url": "https://api.github.com/users/flutter/events{/privacy}",
- "received_events_url": "https://api.github.com/users/flutter/received_events",
- "type": "User",
- "site_admin": false
- }
-}''';
-}
-
-PushMessage generateCheckRunEvent({
- String action = 'created',
- int numberOfPullRequests = 1,
-}) {
- String data = '''{
- "action": "$action",
- "check_run": {
- "id": 128620228,
- "node_id": "MDg6Q2hlY2tSdW4xMjg2MjAyMjg=",
- "head_sha": "ec26c3e57ca3a959ca5aad62de7213c562f8c821",
- "external_id": "",
- "url": "https://api.github.com/repos/flutter/flutter/check-runs/128620228",
- "html_url": "https://github.com/flutter/flutter/runs/128620228",
- "details_url": "https://octocoders.io",
- "status": "queued",
- "conclusion": null,
- "started_at": "2019-05-15T15:21:12Z",
- "completed_at": null,
- "output": {
- "title": null,
- "summary": null,
- "text": null,
- "annotations_count": 0,
- "annotations_url": "https://api.github.com/repos/flutter/flutter/check-runs/128620228/annotations"
- },
- "name": "Octocoders-linter",
- "check_suite": {
- "id": 118578147,
- "node_id": "MDEwOkNoZWNrU3VpdGUxMTg1NzgxNDc=",
- "head_branch": "changes",
- "head_sha": "ec26c3e57ca3a959ca5aad62de7213c562f8c821",
- "status": "queued",
- "conclusion": null,
- "url": "https://api.github.com/repos/flutter/flutter/check-suites/118578147",
- "before": "6113728f27ae82c7b1a177c8d03f9e96e0adf246",
- "after": "ec26c3e57ca3a959ca5aad62de7213c562f8c821",
- "pull_requests": [
- {
- "url": "https://api.github.com/repos/flutter/flutter/pulls/2",
- "id": 279147437,
- "number": 2,
- "head": {
- "ref": "changes",
- "sha": "ec26c3e57ca3a959ca5aad62de7213c562f8c821",
- "repo": {
- "id": 186853002,
- "url": "https://api.github.com/repos/flutter/flutter",
- "name": "flutter"
- }
- },
- "base": {
- "ref": "master",
- "sha": "f95f852bd8fca8fcc58a9a2d6c842781e32a215e",
- "repo": {
- "id": 186853002,
- "url": "https://api.github.com/repos/flutter/flutter",
- "name": "flutter"
- }
- }
- }
- ],
- "app": {
- "id": 29310,
- "node_id": "MDM6QXBwMjkzMTA=",
- "owner": {
- "login": "Octocoders",
- "id": 38302899,
- "node_id": "MDEyOk9yZ2FuaXphdGlvbjM4MzAyODk5",
- "avatar_url": "https://avatars1.githubusercontent.com/u/38302899?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/Octocoders",
- "html_url": "https://github.com/Octocoders",
- "followers_url": "https://api.github.com/users/Octocoders/followers",
- "following_url": "https://api.github.com/users/Octocoders/following{/other_user}",
- "gists_url": "https://api.github.com/users/Octocoders/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/Octocoders/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/Octocoders/subscriptions",
- "organizations_url": "https://api.github.com/users/Octocoders/orgs",
- "repos_url": "https://api.github.com/users/Octocoders/repos",
- "events_url": "https://api.github.com/users/Octocoders/events{/privacy}",
- "received_events_url": "https://api.github.com/users/Octocoders/received_events",
- "type": "Organization",
- "site_admin": false
- },
- "name": "octocoders-linter",
- "description": "",
- "external_url": "https://octocoders.io",
- "html_url": "https://github.com/apps/octocoders-linter",
- "created_at": "2019-04-19T19:36:24Z",
- "updated_at": "2019-04-19T19:36:56Z",
- "permissions": {
- "administration": "write",
- "checks": "write",
- "contents": "write",
- "deployments": "write",
- "issues": "write",
- "members": "write",
- "metadata": "read",
- "organization_administration": "write",
- "organization_hooks": "write",
- "organization_plan": "read",
- "organization_projects": "write",
- "organization_user_blocking": "write",
- "pages": "write",
- "pull_requests": "write",
- "repository_hooks": "write",
- "repository_projects": "write",
- "statuses": "write",
- "team_discussions": "write",
- "vulnerability_alerts": "read"
- },
- "events": []
- },
- "created_at": "2019-05-15T15:20:31Z",
- "updated_at": "2019-05-15T15:20:31Z"
- },
- "app": {
- "id": 29310,
- "node_id": "MDM6QXBwMjkzMTA=",
- "owner": {
- "login": "Octocoders",
- "id": 38302899,
- "node_id": "MDEyOk9yZ2FuaXphdGlvbjM4MzAyODk5",
- "avatar_url": "https://avatars1.githubusercontent.com/u/38302899?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/Octocoders",
- "html_url": "https://github.com/Octocoders",
- "followers_url": "https://api.github.com/users/Octocoders/followers",
- "following_url": "https://api.github.com/users/Octocoders/following{/other_user}",
- "gists_url": "https://api.github.com/users/Octocoders/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/Octocoders/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/Octocoders/subscriptions",
- "organizations_url": "https://api.github.com/users/Octocoders/orgs",
- "repos_url": "https://api.github.com/users/Octocoders/repos",
- "events_url": "https://api.github.com/users/Octocoders/events{/privacy}",
- "received_events_url": "https://api.github.com/users/Octocoders/received_events",
- "type": "Organization",
- "site_admin": false
- },
- "name": "octocoders-linter",
- "description": "",
- "external_url": "https://octocoders.io",
- "html_url": "https://github.com/apps/octocoders-linter",
- "created_at": "2019-04-19T19:36:24Z",
- "updated_at": "2019-04-19T19:36:56Z",
- "permissions": {
- "administration": "write",
- "checks": "write",
- "contents": "write",
- "deployments": "write",
- "issues": "write",
- "members": "write",
- "metadata": "read",
- "organization_administration": "write",
- "organization_hooks": "write",
- "organization_plan": "read",
- "organization_projects": "write",
- "organization_user_blocking": "write",
- "pages": "write",
- "pull_requests": "write",
- "repository_hooks": "write",
- "repository_projects": "write",
- "statuses": "write",
- "team_discussions": "write",
- "vulnerability_alerts": "read"
- },
- "events": []
- },
- "pull_requests": [''';
-
- for (int i = 0; i < numberOfPullRequests; i++) {
- data += '''{
- "url": "https://api.github.com/repos/flutter/flutter/pulls/2",
- "id": 279147437,
- "number": ${i + 2},
- "head": {
- "ref": "changes",
- "sha": "ec26c3e57ca3a959ca5aad62de7213c562f8c821",
- "repo": {
- "id": 186853002,
- "url": "https://api.github.com/repos/flutter/flutter",
- "name": "flutter"
- }
- },
- "base": {
- "ref": "master",
- "sha": "f95f852bd8fca8fcc58a9a2d6c842781e32a215e",
- "repo": {
- "id": 186853002,
- "url": "https://api.github.com/repos/flutter/flutter",
- "name": "flutter"
- }
- }
- }''';
- if (i < numberOfPullRequests - 1) {
- data += ',';
- }
- }
- data += '''],
- "deployment": {
- "url": "https://api.github.com/repos/flutter/flutter/deployments/326191728",
- "id": 326191728,
- "node_id": "MDEwOkRlcGxveW1lbnQzMjYxOTE3Mjg=",
- "task": "deploy",
- "original_environment": "lab",
- "environment": "lab",
- "description": null,
- "created_at": "2021-02-18T08:22:48Z",
- "updated_at": "2021-02-18T09:47:16Z",
- "statuses_url": "https://api.github.com/repos/flutter/flutter/deployments/326191728/statuses",
- "repository_url": "https://api.github.com/repos/flutter/flutter"
- }
- },
- "repository": {
- "id": 186853002,
- "node_id": "MDEwOlJlcG9zaXRvcnkxODY4NTMwMDI=",
- "name": "flutter",
- "full_name": "flutter/flutter",
- "private": false,
- "owner": {
- "login": "flutter",
- "id": 21031067,
- "node_id": "MDQ6VXNlcjIxMDMxMDY3",
- "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/flutter",
- "html_url": "https://github.com/flutter",
- "followers_url": "https://api.github.com/users/flutter/followers",
- "following_url": "https://api.github.com/users/flutter/following{/other_user}",
- "gists_url": "https://api.github.com/users/flutter/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/flutter/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/flutter/subscriptions",
- "organizations_url": "https://api.github.com/users/flutter/orgs",
- "repos_url": "https://api.github.com/users/flutter/repos",
- "events_url": "https://api.github.com/users/flutter/events{/privacy}",
- "received_events_url": "https://api.github.com/users/flutter/received_events",
- "type": "User",
- "site_admin": false
- },
- "html_url": "https://github.com/flutter/flutter",
- "description": null,
- "fork": false,
- "url": "https://api.github.com/repos/flutter/flutter",
- "forks_url": "https://api.github.com/repos/flutter/flutter/forks",
- "keys_url": "https://api.github.com/repos/flutter/flutter/keys{/key_id}",
- "collaborators_url": "https://api.github.com/repos/flutter/flutter/collaborators{/collaborator}",
- "teams_url": "https://api.github.com/repos/flutter/flutter/teams",
- "hooks_url": "https://api.github.com/repos/flutter/flutter/hooks",
- "issue_events_url": "https://api.github.com/repos/flutter/flutter/issues/events{/number}",
- "events_url": "https://api.github.com/repos/flutter/flutter/events",
- "assignees_url": "https://api.github.com/repos/flutter/flutter/assignees{/user}",
- "branches_url": "https://api.github.com/repos/flutter/flutter/branches{/branch}",
- "tags_url": "https://api.github.com/repos/flutter/flutter/tags",
- "blobs_url": "https://api.github.com/repos/flutter/flutter/git/blobs{/sha}",
- "git_tags_url": "https://api.github.com/repos/flutter/flutter/git/tags{/sha}",
- "git_refs_url": "https://api.github.com/repos/flutter/flutter/git/refs{/sha}",
- "trees_url": "https://api.github.com/repos/flutter/flutter/git/trees{/sha}",
- "statuses_url": "https://api.github.com/repos/flutter/flutter/statuses/{sha}",
- "languages_url": "https://api.github.com/repos/flutter/flutter/languages",
- "stargazers_url": "https://api.github.com/repos/flutter/flutter/stargazers",
- "contributors_url": "https://api.github.com/repos/flutter/flutter/contributors",
- "subscribers_url": "https://api.github.com/repos/flutter/flutter/subscribers",
- "subscription_url": "https://api.github.com/repos/flutter/flutter/subscription",
- "commits_url": "https://api.github.com/repos/flutter/flutter/commits{/sha}",
- "git_commits_url": "https://api.github.com/repos/flutter/flutter/git/commits{/sha}",
- "comments_url": "https://api.github.com/repos/flutter/flutter/comments{/number}",
- "issue_comment_url": "https://api.github.com/repos/flutter/flutter/issues/comments{/number}",
- "contents_url": "https://api.github.com/repos/flutter/flutter/contents/{+path}",
- "compare_url": "https://api.github.com/repos/flutter/flutter/compare/{base}...{head}",
- "merges_url": "https://api.github.com/repos/flutter/flutter/merges",
- "archive_url": "https://api.github.com/repos/flutter/flutter/{archive_format}{/ref}",
- "downloads_url": "https://api.github.com/repos/flutter/flutter/downloads",
- "issues_url": "https://api.github.com/repos/flutter/flutter/issues{/number}",
- "pulls_url": "https://api.github.com/repos/flutter/flutter/pulls{/number}",
- "milestones_url": "https://api.github.com/repos/flutter/flutter/milestones{/number}",
- "notifications_url": "https://api.github.com/repos/flutter/flutter/notifications{?since,all,participating}",
- "labels_url": "https://api.github.com/repos/flutter/flutter/labels{/name}",
- "releases_url": "https://api.github.com/repos/flutter/flutter/releases{/id}",
- "deployments_url": "https://api.github.com/repos/flutter/flutter/deployments",
- "created_at": "2019-05-15T15:19:25Z",
- "updated_at": "2019-05-15T15:21:03Z",
- "pushed_at": "2019-05-15T15:20:57Z",
- "git_url": "git://github.com/flutter/flutter.git",
- "ssh_url": "git@github.com:flutter/flutter.git",
- "clone_url": "https://github.com/flutter/flutter.git",
- "svn_url": "https://github.com/flutter/flutter",
- "homepage": null,
- "size": 0,
- "stargazers_count": 0,
- "watchers_count": 0,
- "language": "Ruby",
- "has_issues": true,
- "has_projects": true,
- "has_downloads": true,
- "has_wiki": true,
- "has_pages": true,
- "forks_count": 1,
- "mirror_url": null,
- "archived": false,
- "disabled": false,
- "open_issues_count": 2,
- "license": null,
- "forks": 1,
- "open_issues": 2,
- "watchers": 0,
- "default_branch": "master"
- },
- "sender": {
- "login": "flutter",
- "id": 21031067,
- "node_id": "MDQ6VXNlcjIxMDMxMDY3",
- "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/flutter",
- "html_url": "https://github.com/flutter",
- "followers_url": "https://api.github.com/users/flutter/followers",
- "following_url": "https://api.github.com/users/flutter/following{/other_user}",
- "gists_url": "https://api.github.com/users/flutter/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/flutter/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/flutter/subscriptions",
- "organizations_url": "https://api.github.com/users/flutter/orgs",
- "repos_url": "https://api.github.com/users/flutter/repos",
- "events_url": "https://api.github.com/users/flutter/events{/privacy}",
- "received_events_url": "https://api.github.com/users/flutter/received_events",
- "type": "User",
- "site_admin": false
- }
-}''';
- final pb.GithubWebhookMessage message = pb.GithubWebhookMessage(
- event: 'check_run',
- payload: data,
- );
- return PushMessage(
- data: message.writeToJson(),
- messageId: 'abc123',
- );
-}
-
-PushMessage generateCreateBranchMessage(String branchName, String repository, {bool forked = false}) {
- final CreateEvent createEvent = generateCreateBranchEvent(branchName, repository, forked: forked);
- final pb.GithubWebhookMessage message = pb.GithubWebhookMessage(
- event: 'create',
- payload: jsonEncode(createEvent),
- );
- return PushMessage(data: message.writeToJson(), messageId: 'abc123');
-}
-
-CreateEvent generateCreateBranchEvent(String branchName, String repository, {bool forked = false}) =>
- CreateEvent.fromJson(
- jsonDecode('''
-{
- "ref": "$branchName",
- "ref_type": "branch",
- "master_branch": "master",
- "description": null,
- "pusher_type": "user",
- "repository": {
- "id": 186853002,
- "node_id": "MDEwOlJlcG9zaXRvcnkxODY4NTMwMDI=",
- "name": "${repository.split('/')[1]}",
- "full_name": "$repository",
- "private": false,
- "owner": {
- "login": "${repository.split('/')[0]}",
- "id": 21031067,
- "node_id": "MDQ6VXNlcjIxMDMxMDY3",
- "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/Codertocat",
- "html_url": "https://github.com/Codertocat",
- "followers_url": "https://api.github.com/users/Codertocat/followers",
- "following_url": "https://api.github.com/users/Codertocat/following{/other_user}",
- "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions",
- "organizations_url": "https://api.github.com/users/Codertocat/orgs",
- "repos_url": "https://api.github.com/users/Codertocat/repos",
- "events_url": "https://api.github.com/users/Codertocat/events{/privacy}",
- "received_events_url": "https://api.github.com/users/Codertocat/received_events",
- "type": "User",
- "site_admin": false
- },
- "html_url": "https://github.com/$repository",
- "description": null,
- "fork": $forked,
- "url": "https://api.github.com/repos/$repository",
- "forks_url": "https://api.github.com/repos/$repository/forks",
- "keys_url": "https://api.github.com/repos/$repository/keys{/key_id}",
- "collaborators_url": "https://api.github.com/repos/$repository/collaborators{/collaborator}",
- "teams_url": "https://api.github.com/repos/$repository/teams",
- "hooks_url": "https://api.github.com/repos/$repository/hooks",
- "issue_events_url": "https://api.github.com/repos/$repository/issues/events{/number}",
- "events_url": "https://api.github.com/repos/$repository/events",
- "assignees_url": "https://api.github.com/repos/$repository/assignees{/user}",
- "branches_url": "https://api.github.com/repos/$repository/branches{/branch}",
- "tags_url": "https://api.github.com/repos/$repository/tags",
- "blobs_url": "https://api.github.com/repos/$repository/git/blobs{/sha}",
- "git_tags_url": "https://api.github.com/repos/$repository/git/tags{/sha}",
- "git_refs_url": "https://api.github.com/repos/$repository/git/refs{/sha}",
- "trees_url": "https://api.github.com/repos/$repository/git/trees{/sha}",
- "statuses_url": "https://api.github.com/repos/$repository/statuses/{sha}",
- "languages_url": "https://api.github.com/repos/$repository/languages",
- "stargazers_url": "https://api.github.com/repos/$repository/stargazers",
- "contributors_url": "https://api.github.com/repos/$repository/contributors",
- "subscribers_url": "https://api.github.com/repos/$repository/subscribers",
- "subscription_url": "https://api.github.com/repos/$repository/subscription",
- "commits_url": "https://api.github.com/repos/$repository/commits{/sha}",
- "git_commits_url": "https://api.github.com/repos/$repository/git/commits{/sha}",
- "comments_url": "https://api.github.com/repos/$repository/comments{/number}",
- "issue_comment_url": "https://api.github.com/repos/$repository/issues/comments{/number}",
- "contents_url": "https://api.github.com/repos/$repository/contents/{+path}",
- "compare_url": "https://api.github.com/repos/$repository/compare/{base}...{head}",
- "merges_url": "https://api.github.com/repos/$repository/merges",
- "archive_url": "https://api.github.com/repos/$repository/{archive_format}{/ref}",
- "downloads_url": "https://api.github.com/repos/$repository/downloads",
- "issues_url": "https://api.github.com/repos/$repository/issues{/number}",
- "pulls_url": "https://api.github.com/repos/$repository/pulls{/number}",
- "milestones_url": "https://api.github.com/repos/$repository/milestones{/number}",
- "notifications_url": "https://api.github.com/repos/$repository/notifications{?since,all,participating}",
- "labels_url": "https://api.github.com/repos/$repository/labels{/name}",
- "releases_url": "https://api.github.com/repos/$repository/releases{/id}",
- "deployments_url": "https://api.github.com/repos/$repository/deployments",
- "created_at": "2019-05-15T15:19:25Z",
- "updated_at": "2019-05-15T15:20:41Z",
- "pushed_at": "2019-05-15T15:20:56Z",
- "git_url": "git://github.com/$repository.git",
- "ssh_url": "git@github.com:Codertocat/Hello-World.git",
- "clone_url": "https://github.com/$repository.git",
- "svn_url": "https://github.com/$repository",
- "homepage": null,
- "size": 0,
- "stargazers_count": 0,
- "watchers_count": 0,
- "language": "Ruby",
- "has_issues": true,
- "has_projects": true,
- "has_downloads": true,
- "has_wiki": true,
- "has_pages": true,
- "forks_count": 1,
- "mirror_url": null,
- "archived": false,
- "disabled": false,
- "open_issues_count": 2,
- "license": null,
- "forks": 1,
- "open_issues": 2,
- "watchers": 0,
- "default_branch": "master"
- },
- "sender": {
- "login": "Codertocat",
- "id": 21031067,
- "node_id": "MDQ6VXNlcjIxMDMxMDY3",
- "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/Codertocat",
- "html_url": "https://github.com/Codertocat",
- "followers_url": "https://api.github.com/users/Codertocat/followers",
- "following_url": "https://api.github.com/users/Codertocat/following{/other_user}",
- "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}",
- "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}",
- "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions",
- "organizations_url": "https://api.github.com/users/Codertocat/orgs",
- "repos_url": "https://api.github.com/users/Codertocat/repos",
- "events_url": "https://api.github.com/users/Codertocat/events{/privacy}",
- "received_events_url": "https://api.github.com/users/Codertocat/received_events",
- "type": "User",
- "site_admin": false
- }
-}''') as Map<String, dynamic>,
- );
-
-PushMessage generatePushMessage(String branch, String organization, String repository) {
- final Map<String, dynamic> event = generatePushEvent(branch, organization, repository);
- final pb.GithubWebhookMessage message = pb.GithubWebhookMessage(event: 'push', payload: jsonEncode(event));
- return PushMessage(data: message.writeToJson(), messageId: 'abc123');
-}
-
-Map<String, dynamic> generatePushEvent(
- String branch,
- String organization,
- String repository, {
- String sha = 'def456def456def456',
- String message = 'Commit-message',
- String avatarUrl = 'https://fakegithubcontent.com/google_profile',
- String username = 'googledotcom',
-}) =>
- jsonDecode('''
-{
- "ref": "refs/heads/$branch",
- "before": "abc123abc123abc123",
- "after": "$sha",
- "sender": {
- "login": "$username",
- "avatar_url": "$avatarUrl"
- },
- "commits": [
- {
- "id": "ba2f6608108d174c4a6e6e093a4ddcf313656748",
- "message": "Adding null safety",
- "timestamp": "2023-09-05T15:01:04-05:00",
- "url": "https://github.com/org/repo/commit/abc123abc123abc123"
- }
- ],
- "head_commit": {
- "id": "$sha",
- "message": "$message",
- "timestamp": "2023-09-05T15:01:04-05:00",
- "url": "https://github.com/org/repo/commit/abc123abc123abc123"
- },
- "repository": {
- "name": "$repository",
- "full_name": "$organization/$repository"
- }
-}
-''');
diff --git a/app_dart/tool/build.sh b/app_dart/tool/build.sh
deleted file mode 100755
index b253343..0000000
--- a/app_dart/tool/build.sh
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/usr/bin/env bash
-# Copyright 2020 The Flutter Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# Fetches corresponding dart sdk from CIPD for different platforms, builds
-# an executable binary of bin/generate_jspb.dart to `build` folder.
-#
-# This only supports Linux, but can be generalized for Windows and Mac.
-
-set -e
-
-command -v cipd > /dev/null || {
- echo "Please install CIPD (available from depot_tools) and add to path first.";
- exit -1;
-}
-
-DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd)"
-
-cipd ensure -ensure-file $DIR/ensure_file -root $DIR
-
-pushd $DIR/..
-
-if [[ -d "build" ]]; then
- echo "Please remove the build directory before proceeding"
- exit -1
-fi
-
-mkdir -p build
-tool/dart-sdk/bin/dart pub get
-tool/dart-sdk/bin/dart compile exe bin/generate_jspb.dart -o build/ci_yaml_jspb
-
-# Definitions related to uploading packages to CIPD
-echo "# This file was auto-generated by Cocoon
-# For more information, see https://github.com/flutter/cocoon
-#
-# This file defines information related to CIPD for distribution of generate_jspb.
-package: flutter/ci_yaml/generate_jspb/linux-amd64
-description: Binary to convert a ci.yaml to a JSON proto, which is readable by flutter.googlesource.com/infra.
-data:
- - file: ci_yaml_jspb
-" > build/cipd.yaml
-
-popd
diff --git a/app_dart/tool/ensure_file b/app_dart/tool/ensure_file
deleted file mode 100644
index b537e1f..0000000
--- a/app_dart/tool/ensure_file
+++ /dev/null
@@ -1,3 +0,0 @@
-$ServiceURL https://chrome-infra-packages.appspot.com/
-
-dart/dart-sdk/${os}-${arch} stable
diff --git a/auto_submit/.dockerignore b/auto_submit/.dockerignore
deleted file mode 100644
index 21504f8..0000000
--- a/auto_submit/.dockerignore
+++ /dev/null
@@ -1,9 +0,0 @@
-.dockerignore
-Dockerfile
-build/
-.dart_tool/
-.git/
-.github/
-.gitignore
-.idea/
-.packages
diff --git a/auto_submit/.gitignore b/auto_submit/.gitignore
deleted file mode 100644
index 3c8a157..0000000
--- a/auto_submit/.gitignore
+++ /dev/null
@@ -1,6 +0,0 @@
-# Files and directories created by pub.
-.dart_tool/
-.packages
-
-# Conventional directory for build output.
-build/
diff --git a/auto_submit/CHANGELOG.md b/auto_submit/CHANGELOG.md
deleted file mode 100644
index 8d73078..0000000
--- a/auto_submit/CHANGELOG.md
+++ /dev/null
@@ -1,3 +0,0 @@
-## 0.0.1
-
-- Initial checkin of basic server
diff --git a/auto_submit/Dockerfile b/auto_submit/Dockerfile
deleted file mode 100644
index 27652a3..0000000
--- a/auto_submit/Dockerfile
+++ /dev/null
@@ -1,17 +0,0 @@
-# Copyright 2022 The Flutter Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-
-# Dart Docker official images can be found here: https://hub.docker.com/_/dart
-FROM dart:beta@sha256:88ced76ff4a63e565872df26fe2442f060e3ecf828a272090ad10c79e9d044af
-
-WORKDIR /app
-
-# Copy app source code (except anything in .dockerignore).
-COPY . .
-RUN dart pub get
-
-# Start server.
-EXPOSE 8080
-CMD ["/usr/lib/dart/bin/dart", "/app/bin/server.dart"]
diff --git a/auto_submit/LICENSE b/auto_submit/LICENSE
deleted file mode 100644
index d5384ca..0000000
--- a/auto_submit/LICENSE
+++ /dev/null
@@ -1,27 +0,0 @@
-Copyright 2016 The Flutter Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/auto_submit/README.md b/auto_submit/README.md
deleted file mode 100644
index 59ebc1e..0000000
--- a/auto_submit/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Autosubmit
-
-RFC is available in https://flutter.dev/go/autosubmit
diff --git a/auto_submit/analysis_options.yaml b/auto_submit/analysis_options.yaml
deleted file mode 100644
index a200ec8..0000000
--- a/auto_submit/analysis_options.yaml
+++ /dev/null
@@ -1,9 +0,0 @@
-include: ../analysis_options.yaml
-
-analyzer:
- language:
- strict-raw-types: false # TODO: Remove this lint
-
-linter:
- rules:
- constant_identifier_names: false # we have all capitalized enums in check_for_waiting_pull_requests_test.dart
\ No newline at end of file
diff --git a/auto_submit/app.yaml b/auto_submit/app.yaml
deleted file mode 100644
index 8a10f40..0000000
--- a/auto_submit/app.yaml
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright 2019 The Flutter Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-runtime: custom
-env: flex
-service: auto-submit
-
-resources:
- memory_gb: 2.0
-
-readiness_check:
- path: "/readiness_check"
- check_interval_sec: 20
- timeout_sec: 20
- failure_threshold: 10
- success_threshold: 2
- app_start_timeout_sec: 300
diff --git a/auto_submit/bin/server.dart b/auto_submit/bin/server.dart
deleted file mode 100644
index df9595d..0000000
--- a/auto_submit/bin/server.dart
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright 2022 The Flutter 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:async';
-
-import 'package:appengine/appengine.dart';
-import 'package:auto_submit/helpers.dart';
-import 'package:auto_submit/request_handling/authentication.dart';
-import 'package:auto_submit/requests/check_pull_request.dart';
-import 'package:auto_submit/requests/check_revert_request.dart';
-import 'package:auto_submit/requests/github_webhook.dart';
-import 'package:auto_submit/requests/readiness_check.dart';
-import 'package:auto_submit/service/config.dart';
-import 'package:auto_submit/service/secrets.dart';
-import 'package:neat_cache/neat_cache.dart';
-import 'package:shelf_router/shelf_router.dart';
-
-/// Number of entries allowed in [Cache].
-const int kCacheSize = 1024;
-
-Future<void> main() async {
- await withAppEngineServices(() async {
- useLoggingPackageAdaptor();
-
- final cache = Cache.inMemoryCacheProvider(kCacheSize);
- final Config config = Config(
- cacheProvider: cache,
- secretManager: CloudSecretManager(),
- );
- const CronAuthProvider authProvider = CronAuthProvider();
-
- final Router router = Router()
- ..post(
- '/webhook',
- GithubWebhook(
- config: config,
- ).post,
- )
- ..get(
- '/check-pull-request',
- CheckPullRequest(
- config: config,
- cronAuthProvider: authProvider,
- ).run,
- )
- ..get(
- '/check-revert-requests',
- CheckRevertRequest(
- config: config,
- cronAuthProvider: authProvider,
- ).run,
- )
- ..get(
- '/readiness_check',
- ReadinessCheck(
- config: config,
- ).run,
- );
- await serveHandler(router.call);
- });
-}
diff --git a/auto_submit/build.yaml b/auto_submit/build.yaml
deleted file mode 100644
index fdbbd17..0000000
--- a/auto_submit/build.yaml
+++ /dev/null
@@ -1,8 +0,0 @@
-targets:
- $default:
- builders:
- json_serializable:
- options:
- # Options configure how source code is generated for every
- # `@JsonSerializable`-annotated class in the package.
- field_rename: snake
diff --git a/auto_submit/cloudbuild_auto_submit.yaml b/auto_submit/cloudbuild_auto_submit.yaml
deleted file mode 100644
index 2b65de8..0000000
--- a/auto_submit/cloudbuild_auto_submit.yaml
+++ /dev/null
@@ -1,32 +0,0 @@
-# Provide instructions for google Cloud Build to auto-build flutter
-# auto-submit bot to flutter-dashboard project. Auto-build will be triggered
-# by daily schedule on `main` branch. This cloudbuild calls an additional
-# cloudbuild configuration responsible for deployment.
-#
-# This job is for generating the docker image with build provenance,
-# and the deployment job uses the generated docker image and deploys it to
-# App Engine.
-
-steps:
- # Build docker image
- - name: 'us-docker.pkg.dev/cloud-builders/ga/v1/docker'
- args: ['build', '-t', 'us-docker.pkg.dev/$PROJECT_ID/appengine/auto-submit.version-$SHORT_SHA', 'auto_submit']
-
- # Trigger the cloud build that deploys the docker image
- - name: gcr.io/cloud-builders/gcloud
- entrypoint: '/bin/bash'
- args:
- - '-c'
- - |-
- gcloud builds submit \
- --config auto_submit/cloudbuild_auto_submit_deploy.yaml \
- --substitutions="SHORT_SHA=$SHORT_SHA" \
- --async
-
-timeout: 1200s
-
-images: ['us-docker.pkg.dev/$PROJECT_ID/appengine/auto-submit.version-$SHORT_SHA']
-
-# If build provenance is not generated, the docker deployment will fail.
-options:
- requestedVerifyOption: VERIFIED
diff --git a/auto_submit/cloudbuild_auto_submit_deploy.yaml b/auto_submit/cloudbuild_auto_submit_deploy.yaml
deleted file mode 100644
index 968990a..0000000
--- a/auto_submit/cloudbuild_auto_submit_deploy.yaml
+++ /dev/null
@@ -1,41 +0,0 @@
-# Provide instructions for google Cloud Build to auto-build flutter
-# auto submit to flutter-dashboard project. Auto-build will be triggered
-# by daily schedule on `main` branch.
-#
-# The auto-build will be skipped if no new commits since last deployment.
-
-steps:
- # Get recently pushed docker image and associated provenance, along with the
- # correct docker digest url, including the hash.
- - name: gcr.io/cloud-builders/gcloud
- entrypoint: '/bin/bash'
- args:
- - '-c'
- - |-
- cloud_build/get_docker_image_provenance.sh \
- us-docker.pkg.dev/$PROJECT_ID/appengine/auto-submit.version-$SHORT_SHA:latest \
- unverified_provenance.json
-
- # Verify provenance is valid before proceeding with deployment.
- - name: 'golang:1.20'
- entrypoint: '/bin/bash'
- args:
- - '-c'
- - |-
- cloud_build/verify_provenance.sh unverified_provenance.json
-
- # Deploy a new version to google cloud.
- - name: gcr.io/cloud-builders/gcloud
- entrypoint: '/bin/bash'
- args:
- - '-c'
- - |-
- gcloud config set project $PROJECT_ID
- latest_version=$(gcloud app versions list --hide-no-traffic --format 'value(version.id)')
- if [ "$latest_version" = "version-$SHORT_SHA" ]; then
- echo "No updates since last deployment."
- else
- bash cloud_build/deploy_auto_submit.sh $PROJECT_ID $SHORT_SHA
- fi
-
-timeout: 1200s
diff --git a/auto_submit/lib/action/git_cli_revert_method.dart b/auto_submit/lib/action/git_cli_revert_method.dart
deleted file mode 100644
index 7ee1073..0000000
--- a/auto_submit/lib/action/git_cli_revert_method.dart
+++ /dev/null
@@ -1,93 +0,0 @@
-// Copyright 2023 The Flutter 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:auto_submit/action/revert_method.dart';
-import 'package:auto_submit/configuration/repository_configuration.dart';
-import 'package:auto_submit/git/cli_command.dart';
-import 'package:auto_submit/git/utilities.dart';
-import 'package:auto_submit/git/git_cli.dart';
-import 'package:auto_submit/git/git_repository_manager.dart';
-import 'package:auto_submit/requests/exceptions.dart';
-import 'package:auto_submit/service/config.dart';
-import 'package:auto_submit/service/github_service.dart';
-import 'package:auto_submit/service/log.dart';
-import 'package:auto_submit/service/revert_issue_body_formatter.dart';
-import 'package:github/github.dart' as github;
-import 'package:github/github.dart';
-import 'package:retry/retry.dart';
-
-class GitCliRevertMethod implements RevertMethod {
- @override
- Future<github.PullRequest?> createRevert(
- Config config,
- String initiatingAuthor,
- github.PullRequest pullRequest,
- ) async {
- final github.RepositorySlug slug = pullRequest.base!.repo!.slug();
- final String commitSha = pullRequest.mergeCommitSha!;
- // we will need to collect the pr number after the revert request is generated.
-
- final RepositoryConfiguration repositoryConfiguration = await config.getRepositoryConfiguration(slug);
- final String baseBranch = repositoryConfiguration.defaultBranch;
-
- final String cloneToDirectory = '${slug.name}_$commitSha';
- final GitRepositoryManager gitRepositoryManager = GitRepositoryManager(
- slug: slug,
- workingDirectory: Directory.current.path,
- cloneToDirectory: cloneToDirectory,
- gitCli: GitCli(GitAccessMethod.HTTP, CliCommand()),
- );
-
- // The exception is caught by the thrower.
- try {
- await gitRepositoryManager.cloneRepository();
- await gitRepositoryManager.setupConfig();
- await gitRepositoryManager.revertCommit(baseBranch, commitSha, slug, await config.generateGithubToken(slug));
- } finally {
- await gitRepositoryManager.deleteRepository();
- }
-
- final GitRevertBranchName gitRevertBranchName = GitRevertBranchName(commitSha);
- final GithubService githubService = await config.createGithubService(slug);
-
- const RetryOptions retryOptions =
- RetryOptions(delayFactor: Duration(seconds: 1), maxDelay: Duration(seconds: 1), maxAttempts: 4);
-
- Branch? branch;
- // Attempt a few times to get the branch name. This may not be needed.
- // Let the exception bubble up from here.
- await retryOptions.retry(
- () async {
- branch = await githubService.getBranch(slug, gitRevertBranchName.branch);
- },
- retryIf: (Exception e) => e is NotFoundException,
- );
-
- log.info('found branch ${slug.fullName}/${branch!.name}, safe to create revert request of ${pullRequest.number!}.');
-
- final RevertIssueBodyFormatter formatter = RevertIssueBodyFormatter(
- slug: slug,
- originalPrNumber: pullRequest.number!,
- initiatingAuthor: initiatingAuthor,
- originalPrTitle: pullRequest.title,
- originalPrBody: pullRequest.body,
- ).format;
-
- log.info('Attempting to create pull request with ${slug.fullName}/${gitRevertBranchName.branch}.');
- final github.PullRequest revertPullRequest = await githubService.createPullRequest(
- slug: slug,
- title: formatter.revertPrTitle,
- head: gitRevertBranchName.branch,
- base: baseBranch,
- draft: false,
- body: formatter.revertPrBody,
- );
-
- log.info('pull request number is: ${slug.fullName}/${revertPullRequest.number}');
-
- return revertPullRequest;
- }
-}
diff --git a/auto_submit/lib/action/revert_method.dart b/auto_submit/lib/action/revert_method.dart
deleted file mode 100644
index 22d58c6..0000000
--- a/auto_submit/lib/action/revert_method.dart
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2023 The Flutter 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 'package:auto_submit/service/config.dart';
-import 'package:github/github.dart' as github;
-
-abstract class RevertMethod {
- // Allows substitution of the method of creating the revert request.
- Future<Object?> createRevert(Config config, String initiatingAuthor, github.PullRequest pullRequest);
-}
diff --git a/auto_submit/lib/configuration/repository_configuration.dart b/auto_submit/lib/configuration/repository_configuration.dart
deleted file mode 100644
index 7eea7d1..0000000
--- a/auto_submit/lib/configuration/repository_configuration.dart
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright 2023 The Flutter 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 'package:auto_submit/exception/configuration_exception.dart';
-import 'package:yaml/yaml.dart';
-
-/// The RepositoryConfiguration stores the pertinent information that autosubmit
-/// will need when submiting and validating pull requests for a particular
-/// repository.
-class RepositoryConfiguration {
- // Autosubmit configuration keys as found in the both the global and local
- // yaml configuraiton files.
- static const String allowConfigOverrideKey = 'allow_config_override';
- static const String defaultBranchKey = 'default_branch';
- static const String autoApprovalAccountsKey = 'auto_approval_accounts';
- static const String approvingReviewsKey = 'approving_reviews';
- static const String approvalGroupKey = 'approval_group';
- static const String runCiKey = 'run_ci';
- static const String supportNoReviewRevertKey = 'support_no_review_revert';
- static const String requiredCheckRunsOnRevertKey = 'required_checkruns_on_revert';
-
- static const String defaultBranchStr = 'default';
-
- RepositoryConfiguration({
- allowConfigOverride,
- defaultBranch,
- autoApprovalAccounts,
- approvingReviews,
- approvalGroup,
- runCi,
- supportNoReviewReverts,
- requiredCheckRunsOnRevert,
- }) : allowConfigOverride = allowConfigOverride ?? false,
- defaultBranch = defaultBranch ?? defaultBranchStr,
- autoApprovalAccounts = autoApprovalAccounts ?? <String>{},
- approvingReviews = approvingReviews ?? 2,
- approvalGroup = approvalGroup ?? 'flutter-hackers',
- runCi = runCi ?? true,
- supportNoReviewReverts = supportNoReviewReverts ?? true,
- requiredCheckRunsOnRevert = requiredCheckRunsOnRevert ?? <String>{};
-
- /// This flag allows the repository to override the org level configuration.
- bool allowConfigOverride;
-
- /// The default branch that pull requests will be merged into.
- String defaultBranch;
-
- /// The accounts that have auto approval on their pull requests.
- Set<String> autoApprovalAccounts;
-
- /// The number of reviews needed for a pull request. If the reviewer is part
- /// of the approval group they will need ([approvingReviews] - 1) number of
- /// reviews in order to merge the pull request, if they are not part of the
- /// approval group the will need [approvingReviews] number of reviews.
- int approvingReviews;
-
- /// The group that the pull request author will need pull requests from.
- String approvalGroup;
-
- /// Flag to determine whether or not to wait for all the ci checks to finish
- /// before allowing a merge of the pull request.
- bool runCi;
-
- /// Flag that determines if reverts are allowed without a review.
- bool supportNoReviewReverts;
-
- /// Set of checkruns that must complete before a revert pull request can be
- /// merged.
- Set<String> requiredCheckRunsOnRevert;
-
- @override
- String toString() {
- final StringBuffer stringBuffer = StringBuffer();
- stringBuffer.writeln('$allowConfigOverrideKey: $allowConfigOverride');
- stringBuffer.writeln('$defaultBranchKey: $defaultBranch');
- stringBuffer.writeln('$autoApprovalAccountsKey:');
- for (String account in autoApprovalAccounts) {
- stringBuffer.writeln(' - $account');
- }
- stringBuffer.writeln('$approvingReviewsKey: $approvingReviews');
- stringBuffer.writeln('$approvalGroupKey: $approvalGroup');
- stringBuffer.writeln('$runCiKey: $runCi');
- stringBuffer.writeln('$supportNoReviewRevertKey: $supportNoReviewReverts');
- stringBuffer.writeln('$requiredCheckRunsOnRevertKey:');
- for (String checkrun in requiredCheckRunsOnRevert) {
- stringBuffer.writeln(' - $checkrun');
- }
- return stringBuffer.toString();
- }
-
- static RepositoryConfiguration fromYaml(String yaml) {
- final dynamic yamlDoc = loadYaml(yaml);
-
- final Set<String> autoApprovalAccounts = <String>{};
- final YamlList? yamlAutoApprovalAccounts = yamlDoc[autoApprovalAccountsKey];
- if (yamlAutoApprovalAccounts != null) {
- for (YamlNode element in yamlAutoApprovalAccounts.nodes) {
- autoApprovalAccounts.add(element.value as String);
- }
- }
-
- if (yamlDoc[approvalGroupKey] == null) {
- throw ConfigurationException('The approval group is a required field.');
- }
-
- final Set<String> requiredCheckRunsOnRevert = <String>{};
- final YamlList? yamlRequiredCheckRuns = yamlDoc[requiredCheckRunsOnRevertKey];
- if (yamlRequiredCheckRuns != null) {
- for (YamlNode element in yamlRequiredCheckRuns.nodes) {
- requiredCheckRunsOnRevert.add(element.value as String);
- }
- }
-
- return RepositoryConfiguration(
- allowConfigOverride: yamlDoc[allowConfigOverrideKey],
- defaultBranch: yamlDoc[defaultBranchKey],
- autoApprovalAccounts: autoApprovalAccounts,
- approvingReviews: yamlDoc[approvingReviewsKey],
- approvalGroup: yamlDoc[approvalGroupKey],
- runCi: yamlDoc[runCiKey],
- supportNoReviewReverts: yamlDoc[supportNoReviewRevertKey],
- requiredCheckRunsOnRevert: requiredCheckRunsOnRevert,
- );
- }
-}
diff --git a/auto_submit/lib/configuration/repository_configuration_manager.dart b/auto_submit/lib/configuration/repository_configuration_manager.dart
deleted file mode 100644
index 56f59e0..0000000
--- a/auto_submit/lib/configuration/repository_configuration_manager.dart
+++ /dev/null
@@ -1,167 +0,0 @@
-// Copyright 2023 The Flutter 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 'package:auto_submit/configuration/repository_configuration.dart';
-import 'package:auto_submit/service/config.dart';
-import 'package:auto_submit/service/github_service.dart';
-import 'package:auto_submit/service/log.dart';
-import 'package:github/github.dart';
-import 'package:mutex/mutex.dart';
-import 'package:neat_cache/neat_cache.dart';
-
-/// The [RepositoryConfigurationManager] is responsible for fetching and merging
-/// the autosubmit configuration from the Org level repository and if needed
-/// fetching the override configuration from the pull request repository.
-///
-/// It will attempt to access the cache first before repulling the configuraiton
-/// from the repositories. This is currently set at a 10 minute TTL.
-class RepositoryConfigurationManager {
- RepositoryConfigurationManager(this.config, this.cache);
-
- // Mutex protects the calls to cache while the [RepositoryConfiguration] is
- // collected from github.
- final Mutex _mutex = Mutex();
-
- static const String fileSeparator = '/';
- // This is the well named organization level repository and configuration file
- // we will read before looking to see if there is a local file with
- // overwrites.
- static const String orgRepository = '.github';
- static const String dirName = 'autosubmit';
- static const String fileName = 'autosubmit.yml';
-
- final Config config;
- final Cache cache;
-
- /// Read the configuration from the cache given the slug, if the config is not
- /// in the cache then go and get it from the repository and store it in the
- /// cache.
- Future<RepositoryConfiguration> readRepositoryConfiguration(
- RepositorySlug slug,
- ) async {
- await _mutex.acquire();
- try {
- // Get the contents from the cache or go to github.
- final cacheValue = await cache['${slug.fullName}$fileSeparator$fileName'].get(
- () async => _getConfiguration(slug),
- config.repositoryConfigurationTtl,
- );
- final String cacheYaml = String.fromCharCodes(cacheValue);
- log.info('Converting yaml to RepositoryConfiguration: $cacheYaml');
- return RepositoryConfiguration.fromYaml(cacheYaml);
- } finally {
- _mutex.release();
- }
- }
-
- /// Collect the configuration from github and handle the cache conversion to
- /// bytes.
- Future<List<int>> _getConfiguration(
- RepositorySlug slug,
- ) async {
- // Read the org level configuraiton file first.
- log.info('Getting org level configuration.');
- // Get the Org level configuration.
- final RepositorySlug orgSlug = RepositorySlug(slug.owner, orgRepository);
- GithubService githubService = await config.createGithubService(orgSlug);
- final String orgLevelConfig = await githubService.getFileContents(orgSlug, '$dirName$fileSeparator$fileName');
- final RepositoryConfiguration globalRepositoryConfiguration = RepositoryConfiguration.fromYaml(orgLevelConfig);
-
- // Collect the default branch if it was not supplied.
- if (globalRepositoryConfiguration.defaultBranch == RepositoryConfiguration.defaultBranchStr) {
- globalRepositoryConfiguration.defaultBranch = await githubService.getDefaultBranch(slug);
- }
- log.info('Default branch was found to be ${globalRepositoryConfiguration.defaultBranch} for ${slug.fullName}.');
-
- // If the override flag is set to true we check the pull request's
- // repository to collect any values that will override the global config.
- if (globalRepositoryConfiguration.allowConfigOverride) {
- log.info('Override is set, collecting and merging local repository configuration.');
- githubService = await config.createGithubService(slug);
-
- String? localRepositoryConfigurationYaml;
- try {
- localRepositoryConfigurationYaml = await githubService.getFileContents(slug, '$dirName$fileSeparator$fileName');
- final RepositoryConfiguration localRepositoryConfiguration =
- RepositoryConfiguration.fromYaml(localRepositoryConfigurationYaml);
- final RepositoryConfiguration mergedRepositoryConfiguration = mergeConfigurations(
- globalRepositoryConfiguration,
- localRepositoryConfiguration,
- );
- return mergedRepositoryConfiguration.toString().codeUnits;
- } on GitHubError {
- log.warning(
- 'Configuration override was set but no local repository configuration file was found in ${slug.fullName}, using global configuration.',
- );
- }
- }
-
- return globalRepositoryConfiguration.toString().codeUnits;
- }
-
- /// Merge the local [RepositoryConfiguration] with the global
- /// [RepositoryConfiguration].
- ///
- /// Values that are lists are additive. Values that are not lists overwrite
- /// the value in the global configuration.
- ///
- /// The number of approving reviews in the local configuration cannot override
- /// the global configuration if it is a lower value.
- ///
- /// We also do not need to allow the default branch override as it is
- /// collected from the repository directly.
- RepositoryConfiguration mergeConfigurations(
- RepositoryConfiguration globalConfiguration,
- RepositoryConfiguration localConfiguration,
- ) {
- final RepositoryConfiguration mergedRepositoryConfiguration = RepositoryConfiguration(
- allowConfigOverride: globalConfiguration.allowConfigOverride,
- defaultBranch: globalConfiguration.defaultBranch,
- autoApprovalAccounts: globalConfiguration.autoApprovalAccounts,
- approvingReviews: globalConfiguration.approvingReviews,
- approvalGroup: globalConfiguration.approvalGroup,
- runCi: globalConfiguration.runCi,
- supportNoReviewReverts: globalConfiguration.supportNoReviewReverts,
- requiredCheckRunsOnRevert: globalConfiguration.requiredCheckRunsOnRevert,
- );
-
- // auto approval accounts, they should be empty if nothing was defined
- if (localConfiguration.autoApprovalAccounts.isNotEmpty) {
- mergedRepositoryConfiguration.autoApprovalAccounts.addAll(localConfiguration.autoApprovalAccounts);
- }
-
- // approving reviews
- // this may not be set lower than the global configuration value
- final int localApprovingReviews = localConfiguration.approvingReviews;
- if (localApprovingReviews > globalConfiguration.approvingReviews) {
- mergedRepositoryConfiguration.approvingReviews = localApprovingReviews;
- }
-
- // approval group
- final String localApprovalGroup = localConfiguration.approvalGroup;
- if (localApprovalGroup.isNotEmpty) {
- mergedRepositoryConfiguration.approvalGroup = localApprovalGroup;
- }
-
- // run ci
- // validates the checks runs
- final bool localRunCi = localConfiguration.runCi;
- if (globalConfiguration.runCi != localRunCi) {
- mergedRepositoryConfiguration.runCi = localRunCi;
- }
-
- // support no revert reviews - this will be a moot point after revert is updated
- final bool localSupportNoReviewReverts = localConfiguration.supportNoReviewReverts;
- if (localSupportNoReviewReverts != globalConfiguration.supportNoReviewReverts) {
- mergedRepositoryConfiguration.supportNoReviewReverts = localSupportNoReviewReverts;
- }
-
- // required checkruns on revert, they should be empty if nothing was defined
- if (localConfiguration.requiredCheckRunsOnRevert.isNotEmpty) {
- mergedRepositoryConfiguration.requiredCheckRunsOnRevert.addAll(localConfiguration.requiredCheckRunsOnRevert);
- }
-
- return mergedRepositoryConfiguration;
- }
-}
diff --git a/auto_submit/lib/exception/bigquery_exception.dart b/auto_submit/lib/exception/bigquery_exception.dart
deleted file mode 100644
index 2c79f63..0000000
--- a/auto_submit/lib/exception/bigquery_exception.dart
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2022 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-class BigQueryException implements Exception {
- /// Create a custom exception for Big Query Errors.
- BigQueryException(this.cause);
-
- final String cause;
-
- @override
- String toString() => cause;
-}
diff --git a/auto_submit/lib/exception/configuration_exception.dart b/auto_submit/lib/exception/configuration_exception.dart
deleted file mode 100644
index 00c7d59..0000000
--- a/auto_submit/lib/exception/configuration_exception.dart
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2023 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-class ConfigurationException implements Exception {
- /// Create a custom exception for Autosubmit Configuration Errors.
- ConfigurationException(this.cause);
-
- final String cause;
-
- @override
- String toString() => cause;
-}
diff --git a/auto_submit/lib/exception/retryable_exception.dart b/auto_submit/lib/exception/retryable_exception.dart
deleted file mode 100644
index ba7c3a0..0000000
--- a/auto_submit/lib/exception/retryable_exception.dart
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2022 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/// General exception for retryable error catching.
-class RetryableException implements Exception {
- const RetryableException(this.cause);
-
- final String cause;
-
- @override
- String toString() => cause;
-}
diff --git a/auto_submit/lib/foundation/providers.dart b/auto_submit/lib/foundation/providers.dart
deleted file mode 100644
index 78893e7..0000000
--- a/auto_submit/lib/foundation/providers.dart
+++ /dev/null
@@ -1,16 +0,0 @@
-// Copyright 2022 The Flutter 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 'package:http/http.dart' as http;
-
-/// Signature for a function that returns an [http.Client].
-typedef HttpProvider = http.Client Function();
-
-/// Class that holds static default providers.
-class Providers {
- const Providers._();
-
- /// Creates a [http.Client] that interacts with the internet.
- static http.Client freshHttpClient() => http.Client();
-}
diff --git a/auto_submit/lib/foundation/typedefs.dart b/auto_submit/lib/foundation/typedefs.dart
deleted file mode 100644
index 7573512..0000000
--- a/auto_submit/lib/foundation/typedefs.dart
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:http/http.dart' as http;
-
-/// Signature for a function that returns an [HttpClient].
-///
-/// This is used by [CronAuthProvider] to provide the HTTP client that
-/// will be used (if necessary) to verify OAuth ID tokens (JWT tokens).
-typedef HttpClientProvider = http.Client Function();
diff --git a/auto_submit/lib/git/cli_command.dart b/auto_submit/lib/git/cli_command.dart
deleted file mode 100644
index 5d6e7f1..0000000
--- a/auto_submit/lib/git/cli_command.dart
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2023 The Flutter 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';
-
-class CliCommand {
- CliCommand();
-
- /// Method runs a single command in a shell.
- Future<ProcessResult> runCliCommand({
- required String executable,
- required List<String> arguments,
- bool throwOnError = true,
- String? workingDirectory,
- }) async {
- final process = await Process.start(
- executable,
- arguments,
- workingDirectory: workingDirectory,
- runInShell: true,
- mode: ProcessStartMode.normal,
- );
-
- final result = await Future.wait([
- process.exitCode,
- process.stdout.transform(const SystemEncoding().decoder).join(),
- process.stderr.transform(const SystemEncoding().decoder).join(),
- ]);
-
- final ProcessResult processResult = ProcessResult(
- process.pid,
- result[0] as int,
- result[1] as String,
- result[2] as String,
- );
-
- if (throwOnError) {
- if (processResult.exitCode != 0) {
- final Map<String, String> outputs = {
- if (processResult.stdout != null) 'Standard out': processResult.stdout.toString().trim(),
- if (processResult.stderr != null) 'Standard error': processResult.stderr.toString().trim(),
- }..removeWhere((k, v) => v.isEmpty);
-
- String errorMessage;
- if (outputs.isEmpty) {
- errorMessage = 'Unknown error.';
- } else {
- errorMessage = outputs.entries.map((entry) => '${entry.key}\n${entry.value}').join('\n');
- }
-
- throw ProcessException(executable, arguments, errorMessage, processResult.exitCode);
- }
- }
-
- return processResult;
- }
-}
diff --git a/auto_submit/lib/git/git_cli.dart b/auto_submit/lib/git/git_cli.dart
deleted file mode 100644
index 66caa97..0000000
--- a/auto_submit/lib/git/git_cli.dart
+++ /dev/null
@@ -1,335 +0,0 @@
-// Copyright 2023 The Flutter 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:github/github.dart';
-import 'package:logging/logging.dart';
-import 'cli_command.dart';
-import 'utilities.dart';
-
-/// Class to wrap the command line calls to git.
-class GitCli {
- Logger logger = Logger('RepositoryManager');
-
- static const String git = 'git';
-
- static const String repositoryHttpPrefix = 'https://github.com/';
- static const String repositorySshPrefix = 'git@github.com:';
-
- late String repositoryPrefix;
-
- late CliCommand _cliCommand;
-
- GitCli(GitAccessMethod gitCloneMethod, CliCommand cliCommand) {
- switch (gitCloneMethod) {
- case GitAccessMethod.SSH:
- repositoryPrefix = repositorySshPrefix;
- break;
- case GitAccessMethod.HTTP:
- repositoryPrefix = repositoryHttpPrefix;
- break;
- }
- _cliCommand = cliCommand;
- }
-
- /// Check to see if the current directory is a git repository.
- Future<bool> isGitRepository(String directory) async {
- final ProcessResult processResult = await _cliCommand.runCliCommand(
- executable: git,
- arguments: <String>[
- 'rev-parse',
- ],
- throwOnError: false,
- workingDirectory: directory,
- );
-
- return processResult.exitCode == 0;
- }
-
- /// Checkout repository if it does not currently exist on disk.
- /// We will need to protect against multiple checkouts just in case multiple
- /// calls occur at the same time.
- Future<ProcessResult> cloneRepository({
- required RepositorySlug slug,
- required String workingDirectory,
- required String targetDirectory,
- List<String>? options,
- bool throwOnError = true,
- }) async {
- final List<String> clone = <String>[
- 'clone',
- '$repositoryPrefix${slug.fullName}',
- targetDirectory,
- ];
- if (options != null) {
- clone.addAll(options);
- }
- final ProcessResult processResult = await _cliCommand.runCliCommand(
- executable: git,
- arguments: clone,
- workingDirectory: workingDirectory,
- throwOnError: throwOnError,
- );
-
- return processResult;
- }
-
- Future<ProcessResult> setupUserConfig({
- required RepositorySlug slug,
- required String workingDirectory,
- bool throwOnError = true,
- }) async {
- return _cliCommand.runCliCommand(
- executable: git,
- arguments: <String>[
- 'config',
- '--global',
- 'user.name',
- '"auto-submit[bot]"',
- ],
- workingDirectory: workingDirectory,
- throwOnError: throwOnError,
- );
- }
-
- Future<ProcessResult> setupUserEmailConfig({
- required RepositorySlug slug,
- required String workingDirectory,
- bool throwOnError = true,
- }) async {
- return _cliCommand.runCliCommand(
- executable: git,
- arguments: <String>[
- 'config',
- '--global',
- 'user.email',
- // TODO not sure if the bot will end up needing a valid email.
- // This might also be flutter-auto-submit-bot@google.com
- '"flutter-engprod-team@google.com"',
- ],
- workingDirectory: workingDirectory,
- throwOnError: throwOnError,
- );
- }
-
- /// This is necessary with forked repos but may not be necessary with the bot
- /// as the bot has direct access to the repository.
- Future<ProcessResult> setUpstream({
- required RepositorySlug slug,
- required String workingDirectory,
- required String branchName,
- required String token,
- bool throwOnError = true,
- }) async {
- return _cliCommand.runCliCommand(
- executable: git,
- arguments: <String>[
- 'remote',
- 'set-url',
- 'origin',
- 'https://autosubmit[bot]:$token@github.com/${slug.fullName}.git',
- ],
- workingDirectory: workingDirectory,
- throwOnError: throwOnError,
- );
- }
-
- /// Fetch all new refs for the repository.
- Future<ProcessResult> fetchAll({
- required String workingDirectory,
- bool throwOnError = true,
- }) async {
- return _cliCommand.runCliCommand(
- executable: git,
- arguments: <String>[
- 'fetch',
- '--all',
- ],
- throwOnError: throwOnError,
- );
- }
-
- Future<ProcessResult> pullRebase({
- required String? workingDirectory,
- bool throwOnError = true,
- }) async {
- return _updateRepository(
- workingDirectory: workingDirectory,
- pullMethod: '--rebase',
- throwOnError: throwOnError,
- );
- }
-
- Future<ProcessResult> pullMerge({
- required String? workingDirectory,
- bool throwOnError = true,
- }) async {
- return _updateRepository(
- workingDirectory: workingDirectory,
- pullMethod: '--merge',
- throwOnError: throwOnError,
- );
- }
-
- /// Run the git pull rebase command to keep the repository up to date.
- Future<ProcessResult> _updateRepository({
- required String? workingDirectory,
- required String pullMethod,
- bool throwOnError = true,
- }) async {
- final ProcessResult processResult = await _cliCommand.runCliCommand(
- executable: git,
- arguments: <String>[
- 'pull',
- pullMethod,
- ],
- workingDirectory: workingDirectory,
- );
- return processResult;
- }
-
- /// Checkout and create a branch for the current edit.
- ///
- /// TODO The strategy may be unneccessary here as the bot will not have to
- /// create its own fork of the repo.
- Future<ProcessResult> createBranch({
- required String newBranchName,
- required String workingDirectory,
- bool useCheckout = false,
- bool throwOnError = true,
- }) async {
- // Then create the new branch.
- List<String> args;
- if (useCheckout) {
- args = <String>[
- 'checkout',
- '-b',
- newBranchName,
- ];
- } else {
- args = <String>[
- 'branch',
- newBranchName,
- ];
- }
-
- return _cliCommand.runCliCommand(
- executable: git,
- arguments: args,
- workingDirectory: workingDirectory,
- throwOnError: throwOnError,
- );
- }
-
- /// Revert a pull request commit.
- Future<ProcessResult> revertChange({
- required String commitSha,
- required String workingDirectory,
- bool throwOnError = true,
- }) async {
- // Issue a revert of the pull request.
- return _cliCommand.runCliCommand(
- executable: git,
- arguments: <String>[
- 'revert',
- '--no-edit',
- '-m',
- '1',
- commitSha,
- ],
- workingDirectory: workingDirectory,
- throwOnError: throwOnError,
- );
- }
-
- /// Push changes made to the local branch to github.
- Future<ProcessResult> pushBranch({
- required String branchName,
- required String workingDirectory,
- bool throwOnError = true,
- }) async {
- return _cliCommand.runCliCommand(
- executable: git,
- arguments: <String>[
- 'push',
- '--verbose',
- 'origin',
- branchName,
- ],
- workingDirectory: workingDirectory,
- throwOnError: throwOnError,
- );
- }
-
- /// Delete a local branch from the repo.
- Future<ProcessResult> deleteLocalBranch({
- required String branchName,
- required String workingDirectory,
- bool throwOnError = true,
- }) async {
- return _cliCommand.runCliCommand(
- executable: git,
- arguments: <String>[
- 'branch',
- '-D',
- branchName,
- ],
- workingDirectory: workingDirectory,
- throwOnError: throwOnError,
- );
- }
-
- /// Delete a remote branch from the repo.
- ///
- /// When merging a pull request the pr branch is not automatically deleted.
- Future<ProcessResult> deleteRemoteBranch({
- required String branchName,
- required String workingDirectory,
- bool throwOnError = true,
- }) async {
- return _cliCommand.runCliCommand(
- executable: git,
- arguments: <String>[
- 'push',
- 'origin',
- '--delete',
- branchName,
- ],
- throwOnError: throwOnError,
- );
- }
-
- /// Get the remote origin of the current repository.
- Future<ProcessResult> showOriginUrl({
- required String workingDirectory,
- bool throwOnError = true,
- }) async {
- return _cliCommand.runCliCommand(
- executable: git,
- arguments: <String>[
- 'config',
- '--get',
- 'remote.origin.url',
- ],
- workingDirectory: workingDirectory,
- throwOnError: throwOnError,
- );
- }
-
- Future<ProcessResult> switchBranch({
- required String workingDirectory,
- required String branchName,
- bool throwOnError = true,
- }) async {
- return _cliCommand.runCliCommand(
- executable: git,
- arguments: <String>[
- 'switch',
- branchName,
- ],
- workingDirectory: workingDirectory,
- throwOnError: throwOnError,
- );
- }
-}
diff --git a/auto_submit/lib/git/git_repository_manager.dart b/auto_submit/lib/git/git_repository_manager.dart
deleted file mode 100644
index b5402a7..0000000
--- a/auto_submit/lib/git/git_repository_manager.dart
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 2023 The Flutter 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:auto_submit/service/log.dart';
-import 'package:github/github.dart';
-import 'git_cli.dart';
-import 'utilities.dart';
-
-class GitRepositoryManager {
- final RepositorySlug slug;
- final String workingDirectory;
- String? cloneToDirectory;
- final GitCli gitCli;
-
- late String targetCloneDirectory;
-
- /// RepositoryManager will perform clone, revert and delete on the repository
- /// in the working directory that is cloned to [cloneToDirectory].
- ///
- /// If the clonedToDirectory is not provided then the name of the repository
- /// will be used as the cloneToDirectory.
- GitRepositoryManager({
- required this.slug,
- //path/to/working/directory
- required this.workingDirectory,
- //reponame_commitSha
- this.cloneToDirectory,
- required this.gitCli,
- }) {
- cloneToDirectory ??= slug.name;
- targetCloneDirectory = '$workingDirectory/$cloneToDirectory';
- }
-
- /// Clone the repository identified by the slug.
- ///
- /// Throw out rather than decomposing the return codes from the ProcessResult.
- Future<void> cloneRepository() async {
- if (Directory(targetCloneDirectory).existsSync()) {
- // Blow the directory away instead of trying to update it.
- Directory(targetCloneDirectory).deleteSync(recursive: true);
- }
-
- // Checking out a sparse copy will not checkout source files but will still
- // allow a revert since we only care about the commitSha.
- // Source: https://git-scm.com/docs/git-clone
- await gitCli.cloneRepository(
- slug: slug,
- workingDirectory: workingDirectory,
- targetDirectory: targetCloneDirectory,
- options: ['--sparse'],
- );
- }
-
- Future<void> setupConfig() async {
- await gitCli.setupUserConfig(slug: slug, workingDirectory: targetCloneDirectory);
- await gitCli.setupUserEmailConfig(slug: slug, workingDirectory: targetCloneDirectory);
- }
-
- /// Revert a commit in the current repository.
- ///
- /// The [baseBranchName] is the branch we want to branch from. In this case it
- /// will almost always be the default branch name. The target branch is
- /// preformatted with the commitSha.
- Future<void> revertCommit(String baseBranchName, String commitSha, RepositorySlug slug, String token) async {
- final GitRevertBranchName revertBranchName = GitRevertBranchName(commitSha);
- // Working directory for these must be repo checkout directory.
- // Check out the baseBranchName before doing anything.
- await gitCli.createBranch(
- newBranchName: revertBranchName.branch,
- workingDirectory: targetCloneDirectory,
- useCheckout: true,
- );
-
- await gitCli.setUpstream(
- slug: slug,
- workingDirectory: targetCloneDirectory,
- branchName: revertBranchName.branch,
- token: token,
- );
-
- await gitCli.revertChange(
- commitSha: commitSha,
- workingDirectory: targetCloneDirectory,
- );
-
- await gitCli.pushBranch(
- branchName: revertBranchName.branch,
- workingDirectory: targetCloneDirectory,
- );
- }
-
- /// Delete the repository managed by this instance.
- Future<void> deleteRepository() async {
- log.info('Deleting clone directory $targetCloneDirectory');
- if (Directory(targetCloneDirectory).existsSync()) {
- Directory(targetCloneDirectory).deleteSync(recursive: true);
- }
- }
-}
diff --git a/auto_submit/lib/git/utilities.dart b/auto_submit/lib/git/utilities.dart
deleted file mode 100644
index 3bb8734..0000000
--- a/auto_submit/lib/git/utilities.dart
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2023 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/// There are two ways to clone the repository which will be configurable in the
-/// repository configuration in .github.
-enum GitAccessMethod {
- SSH,
- HTTP,
-}
-
-/// Wrapper class to create a revert branch that is comprised of the prefix
-/// revert_ and the commit sha so the branch is easily identifiable.
-class GitRevertBranchName {
- final String _commitSha;
-
- const GitRevertBranchName(this._commitSha);
-
- static const String _branchPrefix = 'revert';
-
- String get branch => '${_branchPrefix}_$_commitSha';
-}
diff --git a/auto_submit/lib/helpers.dart b/auto_submit/lib/helpers.dart
deleted file mode 100644
index 13240ec..0000000
--- a/auto_submit/lib/helpers.dart
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2022 The Flutter 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:async';
-import 'dart:io';
-
-import 'package:shelf/shelf.dart';
-import 'package:shelf/shelf_io.dart';
-
-import '../service/log.dart';
-
-/// Serves [handler] on [InternetAddress.anyIPv4] using the port returned by
-/// [listenPort].
-///
-/// The returned [Future] will complete using [terminateRequestFuture] after
-/// closing the server.
-Future<void> serveHandler(Handler handler) async {
- final int port = listenPort();
-
- final HttpServer server = await serve(
- handler,
- InternetAddress.anyIPv4, // Allows external connections
- port,
- );
- log.info('Serving at http://${server.address.host}:${server.port}');
-
- await terminateRequestFuture();
-
- await server.close();
-}
-
-/// Returns the port to listen on from environment variable or uses the default
-/// `8080`.
-///
-/// See https://cloud.google.com/run/docs/reference/container-contract#port
-int listenPort() => int.parse(Platform.environment['PORT'] ?? '8080');
-
-/// Returns a [Future] that completes when the process receives a
-/// [ProcessSignal] requesting a shutdown.
-///
-/// [ProcessSignal.sigint] is listened to on all platforms.
-///
-/// [ProcessSignal.sigterm] is listened to on all platforms except Windows.
-Future<void> terminateRequestFuture() {
- final Completer<bool> completer = Completer<bool>.sync();
-
- // sigIntSub is copied below to avoid a race condition - ignoring this lint
- // ignore: cancel_subscriptions
- StreamSubscription? sigIntSub, sigTermSub;
-
- Future<void> signalHandler(ProcessSignal signal) async {
- log.info('Received signal $signal - closing');
-
- final subCopy = sigIntSub;
- if (subCopy != null) {
- sigIntSub = null;
- await subCopy.cancel();
- sigIntSub = null;
- if (sigTermSub != null) {
- await sigTermSub!.cancel();
- sigTermSub = null;
- }
- completer.complete(true);
- }
- }
-
- sigIntSub = ProcessSignal.sigint.watch().listen(signalHandler);
-
- // SIGTERM is not supported on Windows. Attempting to register a SIGTERM
- // handler raises an exception.
- if (!Platform.isWindows) {
- sigTermSub = ProcessSignal.sigterm.watch().listen(signalHandler);
- }
-
- return completer.future;
-}
diff --git a/auto_submit/lib/model/auto_submit_query_result.dart b/auto_submit/lib/model/auto_submit_query_result.dart
deleted file mode 100644
index f83d59e..0000000
--- a/auto_submit/lib/model/auto_submit_query_result.dart
+++ /dev/null
@@ -1,241 +0,0 @@
-// Copyright 2022 The Flutter 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:convert';
-
-import 'package:json_annotation/json_annotation.dart';
-
-part 'auto_submit_query_result.g.dart';
-
-/// The classes in this file are used to serialize/deserialize graphql results.
-/// Using classes rather than complex maps improves the readability of the code
-/// and makes possible to define an extensible interface for the validations.
-
-@JsonSerializable()
-class Author {
- Author({
- this.login,
- });
- final String? login;
-
- factory Author.fromJson(Map<String, dynamic> json) => _$AuthorFromJson(json);
-
- Map<String, dynamic> toJson() => _$AuthorToJson(this);
-}
-
-@JsonSerializable()
-class ReviewNode {
- ReviewNode({
- this.author,
- this.authorAssociation,
- this.state,
- });
- final Author? author;
- @JsonKey(name: 'authorAssociation')
- final String? authorAssociation;
- final String? state;
-
- factory ReviewNode.fromJson(Map<String, dynamic> json) => _$ReviewNodeFromJson(json);
-
- Map<String, dynamic> toJson() => _$ReviewNodeToJson(this);
-}
-
-@JsonSerializable()
-class Reviews {
- Reviews({this.nodes});
-
- List<ReviewNode>? nodes;
-
- factory Reviews.fromJson(Map<String, dynamic> json) => _$ReviewsFromJson(json);
-
- Map<String, dynamic> toJson() => _$ReviewsToJson(this);
-}
-
-@JsonSerializable()
-class CommitNode {
- CommitNode({this.commit});
-
- Commit? commit;
-
- factory CommitNode.fromJson(Map<String, dynamic> json) => _$CommitNodeFromJson(json);
-
- Map<String, dynamic> toJson() => _$CommitNodeToJson(this);
-}
-
-@JsonSerializable()
-class Commits {
- Commits({this.nodes});
-
- List<CommitNode>? nodes;
-
- factory Commits.fromJson(Map<String, dynamic> json) => _$CommitsFromJson(json);
-
- Map<String, dynamic> toJson() => _$CommitsToJson(this);
-}
-
-enum MergeableState {
- CONFLICTING,
- MERGEABLE,
- UNKNOWN,
-}
-
-@JsonSerializable()
-class ContextNode {
- ContextNode({
- this.context,
- this.state,
- this.targetUrl,
- });
-
- String? context;
- String? state;
- @JsonKey(name: 'targetUrl')
- String? targetUrl;
-
- factory ContextNode.fromJson(Map<String, dynamic> json) => _$ContextNodeFromJson(json);
-
- Map<String, dynamic> toJson() => _$ContextNodeToJson(this);
-
- @override
- String toString() => jsonEncode(_$ContextNodeToJson(this));
-}
-
-@JsonSerializable()
-class Status {
- Status({this.contexts});
-
- List<ContextNode>? contexts;
-
- factory Status.fromJson(Map<String, dynamic> json) => _$StatusFromJson(json);
-
- Map<String, dynamic> toJson() => _$StatusToJson(this);
-}
-
-@JsonSerializable()
-class Commit {
- Commit({
- this.abbreviatedOid,
- this.oid,
- this.committedDate,
- this.pushedDate,
- this.status,
- });
- @JsonKey(name: 'abbreviatedOid')
- final String? abbreviatedOid;
- final String? oid;
- @JsonKey(name: 'committedDate')
- final DateTime? committedDate;
- @JsonKey(name: 'pushedDate')
- final DateTime? pushedDate;
- final Status? status;
-
- factory Commit.fromJson(Map<String, dynamic> json) => _$CommitFromJson(json);
-
- Map<String, dynamic> toJson() => _$CommitToJson(this);
-}
-
-@JsonSerializable()
-class PullRequest {
- PullRequest({
- this.author,
- this.authorAssociation,
- this.id,
- this.title,
- this.body,
- this.reviews,
- this.commits,
- this.mergeable,
- this.number,
- });
- final Author? author;
- @JsonKey(name: 'authorAssociation')
- final String? authorAssociation;
- final String? id;
- final String? title;
- final String? body;
- final Reviews? reviews;
- final Commits? commits;
- final int? number;
- // https://docs.github.com/en/graphql/reference/enums#mergeablestate
- final MergeableState? mergeable;
-
- factory PullRequest.fromJson(Map<String, dynamic> json) => _$PullRequestFromJson(json);
-
- Map<String, dynamic> toJson() => _$PullRequestToJson(this);
-}
-
-@JsonSerializable()
-class Repository {
- Repository({
- this.pullRequest,
- });
-
- @JsonKey(name: 'pullRequest')
- PullRequest? pullRequest;
-
- factory Repository.fromJson(Map<String, dynamic> json) => _$RepositoryFromJson(json);
-
- Map<String, dynamic> toJson() => _$RepositoryToJson(this);
-}
-
-@JsonSerializable()
-class QueryResult {
- QueryResult({
- this.repository,
- });
-
- Repository? repository;
-
- factory QueryResult.fromJson(Map<String, dynamic> json) => _$QueryResultFromJson(json);
-
- Map<String, dynamic> toJson() => _$QueryResultToJson(this);
-}
-
-/// The reason for this funky naming scheme can be blamed on GitHub.
-///
-/// See: https://docs.github.com/en/graphql/reference/mutations#revertpullrequest
-/// The enclosing object is called RevertPullRequest and has a nested field also
-/// called RevertPullRequest.
-@JsonSerializable()
-class RevertPullRequest {
- RevertPullRequest({
- this.clientMutationId,
- this.pullRequest,
- this.revertPullRequest,
- });
-
- @JsonKey(name: 'clientMutationId')
- String? clientMutationId;
- @JsonKey(name: 'pullRequest')
- PullRequest? pullRequest;
- @JsonKey(name: 'revertPullRequest')
- PullRequest? revertPullRequest;
-
- factory RevertPullRequest.fromJson(Map<String, dynamic> json) => _$RevertPullRequestFromJson(json);
-
- Map<String, dynamic> toJson() => _$RevertPullRequestToJson(this);
-}
-
-/// This is needed since the data we get is buried within this outer object and
-/// to simplify the deserialization need this wrapper.
-///
-/// The return data is nested as such:
-/// "data": {
-/// "revertPullRequest": {
-/// "clientMutationId": xxx,
-/// "pullRequest": { ... },
-/// "revertPullRequest": { ... }
-/// }
-/// }
-@JsonSerializable()
-class RevertPullRequestData {
- RevertPullRequestData({this.revertPullRequest});
-
- @JsonKey(name: 'revertPullRequest')
- RevertPullRequest? revertPullRequest;
-
- factory RevertPullRequestData.fromJson(Map<String, dynamic> json) => _$RevertPullRequestDataFromJson(json);
-
- Map<String, dynamic> toJson() => _$RevertPullRequestDataToJson(this);
-}
diff --git a/auto_submit/lib/model/auto_submit_query_result.g.dart b/auto_submit/lib/model/auto_submit_query_result.g.dart
deleted file mode 100644
index d2d19b2..0000000
--- a/auto_submit/lib/model/auto_submit_query_result.g.dart
+++ /dev/null
@@ -1,160 +0,0 @@
-// GENERATED CODE - DO NOT MODIFY BY HAND
-
-part of 'auto_submit_query_result.dart';
-
-// **************************************************************************
-// JsonSerializableGenerator
-// **************************************************************************
-
-Author _$AuthorFromJson(Map<String, dynamic> json) => Author(
- login: json['login'] as String?,
- );
-
-Map<String, dynamic> _$AuthorToJson(Author instance) => <String, dynamic>{
- 'login': instance.login,
- };
-
-ReviewNode _$ReviewNodeFromJson(Map<String, dynamic> json) => ReviewNode(
- author: json['author'] == null ? null : Author.fromJson(json['author'] as Map<String, dynamic>),
- authorAssociation: json['authorAssociation'] as String?,
- state: json['state'] as String?,
- );
-
-Map<String, dynamic> _$ReviewNodeToJson(ReviewNode instance) => <String, dynamic>{
- 'author': instance.author,
- 'authorAssociation': instance.authorAssociation,
- 'state': instance.state,
- };
-
-Reviews _$ReviewsFromJson(Map<String, dynamic> json) => Reviews(
- nodes: (json['nodes'] as List<dynamic>?)?.map((e) => ReviewNode.fromJson(e as Map<String, dynamic>)).toList(),
- );
-
-Map<String, dynamic> _$ReviewsToJson(Reviews instance) => <String, dynamic>{
- 'nodes': instance.nodes,
- };
-
-CommitNode _$CommitNodeFromJson(Map<String, dynamic> json) => CommitNode(
- commit: json['commit'] == null ? null : Commit.fromJson(json['commit'] as Map<String, dynamic>),
- );
-
-Map<String, dynamic> _$CommitNodeToJson(CommitNode instance) => <String, dynamic>{
- 'commit': instance.commit,
- };
-
-Commits _$CommitsFromJson(Map<String, dynamic> json) => Commits(
- nodes: (json['nodes'] as List<dynamic>?)?.map((e) => CommitNode.fromJson(e as Map<String, dynamic>)).toList(),
- );
-
-Map<String, dynamic> _$CommitsToJson(Commits instance) => <String, dynamic>{
- 'nodes': instance.nodes,
- };
-
-ContextNode _$ContextNodeFromJson(Map<String, dynamic> json) => ContextNode(
- context: json['context'] as String?,
- state: json['state'] as String?,
- targetUrl: json['targetUrl'] as String?,
- );
-
-Map<String, dynamic> _$ContextNodeToJson(ContextNode instance) => <String, dynamic>{
- 'context': instance.context,
- 'state': instance.state,
- 'targetUrl': instance.targetUrl,
- };
-
-Status _$StatusFromJson(Map<String, dynamic> json) => Status(
- contexts:
- (json['contexts'] as List<dynamic>?)?.map((e) => ContextNode.fromJson(e as Map<String, dynamic>)).toList(),
- );
-
-Map<String, dynamic> _$StatusToJson(Status instance) => <String, dynamic>{
- 'contexts': instance.contexts,
- };
-
-Commit _$CommitFromJson(Map<String, dynamic> json) => Commit(
- abbreviatedOid: json['abbreviatedOid'] as String?,
- oid: json['oid'] as String?,
- committedDate: json['committedDate'] == null ? null : DateTime.parse(json['committedDate'] as String),
- pushedDate: json['pushedDate'] == null ? null : DateTime.parse(json['pushedDate'] as String),
- status: json['status'] == null ? null : Status.fromJson(json['status'] as Map<String, dynamic>),
- );
-
-Map<String, dynamic> _$CommitToJson(Commit instance) => <String, dynamic>{
- 'abbreviatedOid': instance.abbreviatedOid,
- 'oid': instance.oid,
- 'committedDate': instance.committedDate?.toIso8601String(),
- 'pushedDate': instance.pushedDate?.toIso8601String(),
- 'status': instance.status,
- };
-
-PullRequest _$PullRequestFromJson(Map<String, dynamic> json) => PullRequest(
- author: json['author'] == null ? null : Author.fromJson(json['author'] as Map<String, dynamic>),
- authorAssociation: json['authorAssociation'] as String?,
- id: json['id'] as String?,
- title: json['title'] as String?,
- body: json['body'] as String?,
- reviews: json['reviews'] == null ? null : Reviews.fromJson(json['reviews'] as Map<String, dynamic>),
- commits: json['commits'] == null ? null : Commits.fromJson(json['commits'] as Map<String, dynamic>),
- mergeable: $enumDecodeNullable(_$MergeableStateEnumMap, json['mergeable']),
- number: json['number'] as int?,
- );
-
-Map<String, dynamic> _$PullRequestToJson(PullRequest instance) => <String, dynamic>{
- 'author': instance.author,
- 'authorAssociation': instance.authorAssociation,
- 'id': instance.id,
- 'title': instance.title,
- 'body': instance.body,
- 'reviews': instance.reviews,
- 'commits': instance.commits,
- 'number': instance.number,
- 'mergeable': _$MergeableStateEnumMap[instance.mergeable],
- };
-
-const _$MergeableStateEnumMap = {
- MergeableState.CONFLICTING: 'CONFLICTING',
- MergeableState.MERGEABLE: 'MERGEABLE',
- MergeableState.UNKNOWN: 'UNKNOWN',
-};
-
-Repository _$RepositoryFromJson(Map<String, dynamic> json) => Repository(
- pullRequest:
- json['pullRequest'] == null ? null : PullRequest.fromJson(json['pullRequest'] as Map<String, dynamic>),
- );
-
-Map<String, dynamic> _$RepositoryToJson(Repository instance) => <String, dynamic>{
- 'pullRequest': instance.pullRequest,
- };
-
-QueryResult _$QueryResultFromJson(Map<String, dynamic> json) => QueryResult(
- repository: json['repository'] == null ? null : Repository.fromJson(json['repository'] as Map<String, dynamic>),
- );
-
-Map<String, dynamic> _$QueryResultToJson(QueryResult instance) => <String, dynamic>{
- 'repository': instance.repository,
- };
-
-RevertPullRequest _$RevertPullRequestFromJson(Map<String, dynamic> json) => RevertPullRequest(
- clientMutationId: json['clientMutationId'] as String?,
- pullRequest:
- json['pullRequest'] == null ? null : PullRequest.fromJson(json['pullRequest'] as Map<String, dynamic>),
- revertPullRequest: json['revertPullRequest'] == null
- ? null
- : PullRequest.fromJson(json['revertPullRequest'] as Map<String, dynamic>),
- );
-
-Map<String, dynamic> _$RevertPullRequestToJson(RevertPullRequest instance) => <String, dynamic>{
- 'clientMutationId': instance.clientMutationId,
- 'pullRequest': instance.pullRequest,
- 'revertPullRequest': instance.revertPullRequest,
- };
-
-RevertPullRequestData _$RevertPullRequestDataFromJson(Map<String, dynamic> json) => RevertPullRequestData(
- revertPullRequest: json['revertPullRequest'] == null
- ? null
- : RevertPullRequest.fromJson(json['revertPullRequest'] as Map<String, dynamic>),
- );
-
-Map<String, dynamic> _$RevertPullRequestDataToJson(RevertPullRequestData instance) => <String, dynamic>{
- 'revertPullRequest': instance.revertPullRequest,
- };
diff --git a/auto_submit/lib/model/big_query_pull_request_record.dart b/auto_submit/lib/model/big_query_pull_request_record.dart
deleted file mode 100644
index b435f34..0000000
--- a/auto_submit/lib/model/big_query_pull_request_record.dart
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2022 The Flutter 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:convert';
-
-import 'package:json_annotation/json_annotation.dart';
-import 'package:meta/meta.dart';
-
-part 'big_query_pull_request_record.g.dart';
-
-@immutable
-@JsonSerializable()
-class PullRequestRecord {
- const PullRequestRecord({
- this.prCreatedTimestamp,
- this.prLandedTimestamp,
- this.organization,
- this.repository,
- this.author,
- this.prNumber,
- this.prCommit,
- this.prRequestType,
- });
-
- final DateTime? prCreatedTimestamp;
- final DateTime? prLandedTimestamp;
- final String? organization;
- final String? repository;
- final String? author;
- final int? prNumber;
- final String? prCommit;
- final String? prRequestType;
-
- @override
- String toString() => jsonEncode(toJson());
-
- factory PullRequestRecord.fromJson(Map<String, dynamic> json) => _$PullRequestRecordFromJson(json);
-
- Map<String, dynamic> toJson() => _$PullRequestRecordToJson(this);
-}
diff --git a/auto_submit/lib/model/big_query_pull_request_record.g.dart b/auto_submit/lib/model/big_query_pull_request_record.g.dart
deleted file mode 100644
index a898c80..0000000
--- a/auto_submit/lib/model/big_query_pull_request_record.g.dart
+++ /dev/null
@@ -1,31 +0,0 @@
-// GENERATED CODE - DO NOT MODIFY BY HAND
-
-part of 'big_query_pull_request_record.dart';
-
-// **************************************************************************
-// JsonSerializableGenerator
-// **************************************************************************
-
-PullRequestRecord _$PullRequestRecordFromJson(Map<String, dynamic> json) => PullRequestRecord(
- prCreatedTimestamp:
- json['pr_created_timestamp'] == null ? null : DateTime.parse(json['pr_created_timestamp'] as String),
- prLandedTimestamp:
- json['pr_landed_timestamp'] == null ? null : DateTime.parse(json['pr_landed_timestamp'] as String),
- organization: json['organization'] as String?,
- repository: json['repository'] as String?,
- author: json['author'] as String?,
- prNumber: json['pr_number'] as int?,
- prCommit: json['pr_commit'] as String?,
- prRequestType: json['pr_request_type'] as String?,
- );
-
-Map<String, dynamic> _$PullRequestRecordToJson(PullRequestRecord instance) => <String, dynamic>{
- 'pr_created_timestamp': instance.prCreatedTimestamp?.toIso8601String(),
- 'pr_landed_timestamp': instance.prLandedTimestamp?.toIso8601String(),
- 'organization': instance.organization,
- 'repository': instance.repository,
- 'author': instance.author,
- 'pr_number': instance.prNumber,
- 'pr_commit': instance.prCommit,
- 'pr_request_type': instance.prRequestType,
- };
diff --git a/auto_submit/lib/model/pull_request_data_types.dart b/auto_submit/lib/model/pull_request_data_types.dart
deleted file mode 100644
index e85a5bf..0000000
--- a/auto_submit/lib/model/pull_request_data_types.dart
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2022 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/// The type of the change in the pull request we have processed.
-enum PullRequestChangeType {
- /// Merge is any submitted pull request change that does not undo previous changes.
- change,
-
- /// Revert is specifically for undoing changes.
- revert,
-}
-
-/// Values representing the current states of a pull requests we process with
-/// the autosubmit service.
-enum PullRequestState {
- open,
- closed,
-}
diff --git a/auto_submit/lib/request_handling/authentication.dart b/auto_submit/lib/request_handling/authentication.dart
deleted file mode 100644
index d17832a..0000000
--- a/auto_submit/lib/request_handling/authentication.dart
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-import 'dart:io';
-
-import 'package:meta/meta.dart';
-import 'package:shelf/shelf.dart';
-
-import '../requests/exceptions.dart';
-
-/// Class capable of authenticating [HttpRequest]s.
-///
-/// If the request has the `'X-Appengine-Cron'` HTTP header set to "true",
-/// then the request will be authenticated as an App Engine cron job.
-///
-/// The `'X-Appengine-Cron'` HTTP header is set automatically by App Engine
-/// and will be automatically stripped from the request by the App Engine
-/// runtime if the request originated from anything other than a cron job.
-/// Thus, the header is safe to trust as an authentication indicator.
-///
-/// See also:
-///
-/// * <https://cloud.google.com/appengine/docs/standard/python/reference/request-response-headers>
-@immutable
-// TODO(Kristin): Generalize this to implement from a AuthProvider. https://github.com/flutter/flutter/issues/101614
-class CronAuthProvider {
- const CronAuthProvider();
-
- /// Authenticates the specified [request].
- ///
- /// This will throw an [Unauthenticated] exception if the request is
- /// unauthenticated.
- Future<bool> authenticate(Request request) async {
- final Map<String, String> reqHeader = request.headers;
- final bool isCron = reqHeader['X-Appengine-Cron'] == 'true';
- if (isCron) {
- // Authenticate cron requests
- return true;
- }
- throw const Unauthenticated('User is not signed in');
- }
-}
diff --git a/auto_submit/lib/request_handling/pubsub.dart b/auto_submit/lib/request_handling/pubsub.dart
deleted file mode 100644
index 6b3c1a5..0000000
--- a/auto_submit/lib/request_handling/pubsub.dart
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2019 The Flutter 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:convert';
-
-import 'package:auto_submit/service/config.dart';
-import 'package:googleapis/pubsub/v1.dart' as pubsub;
-import 'package:googleapis_auth/auth_io.dart';
-import 'package:http/http.dart';
-
-import '../foundation/providers.dart';
-import '../foundation/typedefs.dart';
-import '../service/log.dart';
-
-/// Service class for interacting with PubSub.
-class PubSub {
- const PubSub({
- this.httpClientProvider = Providers.freshHttpClient,
- });
-
- final HttpClientProvider httpClientProvider;
-
- /// Adds one message to the topic.
- Future<void> publish(String topic, dynamic json) async {
- final Client httpClient = await clientViaApplicationDefaultCredentials(
- scopes: <String>[
- pubsub.PubsubApi.pubsubScope,
- ],
- );
- final pubsub.PubsubApi pubsubApi = pubsub.PubsubApi(httpClient);
- final String messageData = jsonEncode(json);
- final List<int> messageBytes = utf8.encode(messageData);
- final String messageBase64 = base64Encode(messageBytes);
- final pubsub.PublishRequest request = pubsub.PublishRequest(
- messages: <pubsub.PubsubMessage>[
- pubsub.PubsubMessage(data: messageBase64),
- ],
- );
- final String fullTopicName = '${Config.pubsubTopicsPrefix}/$topic';
- final pubsub.PublishResponse response = await pubsubApi.projects.topics.publish(request, fullTopicName);
- log.info('pubsub response messageId=${response.messageIds}');
- }
-
- /// Pulls messages from the server.
- Future<pubsub.PullResponse> pull(String subscription, int maxMessages) async {
- final Client httpClient = await clientViaApplicationDefaultCredentials(
- scopes: <String>[
- pubsub.PubsubApi.pubsubScope,
- ],
- );
- final pubsub.PubsubApi pubsubApi = pubsub.PubsubApi(httpClient);
- final pubsub.PullRequest pullRequest = pubsub.PullRequest(maxMessages: maxMessages);
- final pubsub.PullResponse pullResponse =
- await pubsubApi.projects.subscriptions.pull(pullRequest, '${Config.pubsubSubscriptionsPrefix}/$subscription');
- return pullResponse;
- }
-
- /// Acknowledges the messages associated with the `ack_ids` in the `AcknowledgeRequest`.
- ///
- /// The PubSub system can remove the relevant messages from the subscription.
- Future<void> acknowledge(String subscription, String ackId) async {
- final Client httpClient = await clientViaApplicationDefaultCredentials(
- scopes: <String>[
- pubsub.PubsubApi.pubsubScope,
- ],
- );
- final pubsub.PubsubApi pubsubApi = pubsub.PubsubApi(httpClient);
- final List<String> ackIds = [ackId];
- final pubsub.AcknowledgeRequest acknowledgeRequest = pubsub.AcknowledgeRequest(ackIds: ackIds);
- await pubsubApi.projects.subscriptions
- .acknowledge(acknowledgeRequest, '${Config.pubsubSubscriptionsPrefix}/$subscription');
- }
-}
diff --git a/auto_submit/lib/requests/check_pull_request.dart b/auto_submit/lib/requests/check_pull_request.dart
deleted file mode 100644
index ba46c42..0000000
--- a/auto_submit/lib/requests/check_pull_request.dart
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 2022 The Flutter 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:async';
-import 'dart:convert';
-
-import 'package:auto_submit/requests/check_request.dart';
-import 'package:auto_submit/service/approver_service.dart';
-import 'package:auto_submit/service/log.dart';
-import 'package:auto_submit/service/pull_request_validation_service.dart';
-import 'package:github/github.dart';
-import 'package:googleapis/pubsub/v1.dart' as pub;
-import 'package:shelf/shelf.dart';
-
-import '../request_handling/pubsub.dart';
-
-/// Handler for processing pull requests with 'autosubmit' label.
-///
-/// For pull requests where an 'autosubmit' label was added in pubsub,
-/// check if the pull request is mergable.
-class CheckPullRequest extends CheckRequest {
- const CheckPullRequest({
- required super.config,
- required super.cronAuthProvider,
- super.approverProvider = ApproverService.defaultProvider,
- super.pubsub = const PubSub(),
- });
-
- @override
- Future<Response> get() async {
- return process(
- config.pubsubPullRequestSubscription,
- config.kPubsubPullNumber,
- config.kPullMesssageBatchSize,
- );
- }
-
- ///TODO refactor this method out into the base class.
- /// Process pull request messages from Pubsub.
- Future<Response> process(
- String pubSubSubscription,
- int pubSubPulls,
- int pubSubBatchSize,
- ) async {
- final Set<int> processingLog = <int>{};
- final List<pub.ReceivedMessage> messageList = await pullMessages(
- pubSubSubscription,
- pubSubPulls,
- pubSubBatchSize,
- );
- if (messageList.isEmpty) {
- log.info('No messages are pulled.');
- return Response.ok('No messages are pulled.');
- }
-
- log.info('Processing ${messageList.length} messages');
-
- final PullRequestValidationService validationService = PullRequestValidationService(config);
-
- final List<Future<void>> futures = <Future<void>>[];
-
- for (pub.ReceivedMessage message in messageList) {
- log.info(message.toJson());
- assert(message.message != null);
- assert(message.message!.data != null);
- final String messageData = message.message!.data!;
-
- final Map<String, dynamic> rawBody =
- json.decode(String.fromCharCodes(base64.decode(messageData))) as Map<String, dynamic>;
- log.info('request raw body = $rawBody');
-
- final PullRequest pullRequest = PullRequest.fromJson(rawBody);
-
- log.info('Processing message ackId: ${message.ackId}');
- log.info('Processing mesageId: ${message.message!.messageId}');
- log.info('Processing PR: $rawBody');
- if (processingLog.contains(pullRequest.number)) {
- // Ack duplicate.
- log.info('Ack the duplicated message : ${message.ackId!}.');
- await pubsub.acknowledge(
- pubSubSubscription,
- message.ackId!,
- );
-
- continue;
- } else {
- final ApproverService approver = approverProvider(config);
- log.info('Checking auto approval of pull request: $rawBody');
- await approver.autoApproval(pullRequest);
- processingLog.add(pullRequest.number!);
- }
-
- futures.add(
- validationService.processMessage(pullRequest, message.ackId!, pubsub),
- );
- }
- await Future.wait(futures);
- return Response.ok('Finished processing changes');
- }
-}
diff --git a/auto_submit/lib/requests/check_request.dart b/auto_submit/lib/requests/check_request.dart
deleted file mode 100644
index 8a8f04b..0000000
--- a/auto_submit/lib/requests/check_request.dart
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright 2023 The Flutter 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 'package:auto_submit/server/authenticated_request_handler.dart';
-import 'package:auto_submit/service/approver_service.dart';
-import 'package:googleapis/pubsub/v1.dart' as pub;
-import 'package:shelf/shelf.dart';
-
-import '../request_handling/pubsub.dart';
-
-abstract class CheckRequest extends AuthenticatedRequestHandler {
- const CheckRequest({
- required super.config,
- required super.cronAuthProvider,
- this.approverProvider = ApproverService.defaultProvider,
- this.pubsub = const PubSub(),
- });
-
- final PubSub pubsub;
- final ApproverServiceProvider approverProvider;
-
- @override
- Future<Response> get();
-
- /// Pulls queued Pub/Sub messages.
- ///
- /// Pub/Sub pull request API doesn't guarantee returning all messages each time. This
- /// loops to pull `kPubsubPullNumber` times to try covering all queued messages.
- Future<List<pub.ReceivedMessage>> pullMessages(
- String subscription,
- int pulls,
- int batchSize,
- ) async {
- final Map<String, pub.ReceivedMessage> messageMap = <String, pub.ReceivedMessage>{};
- for (int i = 0; i < pulls; i++) {
- final pub.PullResponse pullResponse = await pubsub.pull(
- subscription,
- batchSize,
- );
- final List<pub.ReceivedMessage>? receivedMessages = pullResponse.receivedMessages;
- if (receivedMessages == null) {
- continue;
- }
- for (pub.ReceivedMessage message in receivedMessages) {
- final String messageId = message.message!.messageId!;
- messageMap[messageId] = message;
- }
- }
- return messageMap.values.toList();
- }
-}
diff --git a/auto_submit/lib/requests/check_revert_request.dart b/auto_submit/lib/requests/check_revert_request.dart
deleted file mode 100644
index 1578a68..0000000
--- a/auto_submit/lib/requests/check_revert_request.dart
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2023 The Flutter 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:convert';
-
-import 'package:auto_submit/request_handling/pubsub.dart';
-import 'package:auto_submit/requests/check_request.dart';
-import 'package:auto_submit/requests/github_pull_request_event.dart';
-import 'package:auto_submit/service/approver_service.dart';
-import 'package:auto_submit/service/log.dart';
-import 'package:auto_submit/service/revert_request_validation_service.dart';
-import 'package:github/github.dart';
-import 'package:googleapis/pubsub/v1.dart' as pub;
-import 'package:shelf/shelf.dart';
-
-/// Handler for processing pull requests with 'revert' label.
-///
-/// For pull requests where an 'revert' label was added in pubsub,
-/// check if the revert request is mergable.
-class CheckRevertRequest extends CheckRequest {
- const CheckRevertRequest({
- required super.config,
- required super.cronAuthProvider,
- super.approverProvider = ApproverService.defaultProvider,
- super.pubsub = const PubSub(),
- });
-
- @override
- Future<Response> get() async {
- /// Currently this is unused and cannot be called.
- return process(
- config.pubsubRevertRequestSubscription,
- config.kPubsubPullNumber,
- config.kPullMesssageBatchSize,
- );
- }
-
- /// Process pull request messages from Pubsub.
- Future<Response> process(
- String pubSubSubscription,
- int pubSubPulls,
- int pubSubBatchSize,
- ) async {
- final Set<int> processingLog = <int>{};
- final List<pub.ReceivedMessage> messageList = await pullMessages(
- pubSubSubscription,
- pubSubPulls,
- pubSubBatchSize,
- );
- if (messageList.isEmpty) {
- log.info('No messages are pulled.');
- return Response.ok('No messages are pulled.');
- }
-
- log.info('Processing ${messageList.length} messages');
-
- final RevertRequestValidationService validationService = RevertRequestValidationService(config);
-
- final List<Future<void>> futures = <Future<void>>[];
-
- for (pub.ReceivedMessage message in messageList) {
- log.info(message.toJson());
- assert(message.message != null);
- assert(message.message!.data != null);
- final String messageData = message.message!.data!;
-
- final Map<String, dynamic> rawBody =
- json.decode(String.fromCharCodes(base64.decode(messageData))) as Map<String, dynamic>;
-
- final GithubPullRequestEvent githubPullRequestEvent = GithubPullRequestEvent.fromJson(rawBody);
- final PullRequest pullRequest = githubPullRequestEvent.pullRequest!;
-
- log.info('Processing message ackId: ${message.ackId}');
- log.info('Processing mesageId: ${message.message!.messageId}');
- log.info('Processing PR: $rawBody');
- if (processingLog.contains(pullRequest.number) || githubPullRequestEvent.action != 'labeled') {
- // Ack duplicate.
- log.info('Ack the duplicated message : ${message.ackId!}.');
- log.info('duplicate pull request #${pullRequest.number}');
- await pubsub.acknowledge(pubSubSubscription, message.ackId!);
- continue;
- } else {
- // Use the auto approval as we do not want to allow non bot reverts to
- // be processed throught the service.
- log.info('new pull request #${pullRequest.number}');
- if (pullRequest.labels!.any((element) => element.name == 'revert of')) {
- final ApproverService approver = approverProvider(config);
- log.info('Checking auto approval of "revert of" pull request: $rawBody');
- await approver.autoApproval(pullRequest);
- } else {
- // These should be closed requests that do not need to be reviewed.
- log.info('Processing "revert" request : ${pullRequest.number}.');
- }
- processingLog.add(pullRequest.number!);
- }
-
- futures.add(
- validationService.processMessage(githubPullRequestEvent, message.ackId!, pubsub),
- );
- }
- await Future.wait(futures);
- return Response.ok('Finished processing changes');
- }
-}
diff --git a/auto_submit/lib/requests/exceptions.dart b/auto_submit/lib/requests/exceptions.dart
deleted file mode 100644
index e40c436..0000000
--- a/auto_submit/lib/requests/exceptions.dart
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2019 The Flutter 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';
-
-/// An exception that may be thrown by a [RequestHandler] to trigger an error
-/// HTTP response.
-class HttpStatusException implements Exception {
- /// Creates a new [HttpStatusException].
- const HttpStatusException(this.statusCode, this.message);
-
- /// The HTTP status code to return to the issuer.
- final int statusCode;
-
- /// The message to show to the issuer to explain the error.
- final String message;
-
- @override
- String toString() => 'HTTP $statusCode: $message';
-}
-
-/// Exception that will trigger an HTTP 400 bad request.
-class BadRequestException extends HttpStatusException {
- const BadRequestException([String message = 'Bad request']) : super(HttpStatus.badRequest, message);
-}
-
-/// Exception that will trigger an HTTP 404 not found
-class NotFoundException extends HttpStatusException {
- const NotFoundException(String missing) : super(HttpStatus.notFound, 'Not found: $missing');
-}
-
-/// Exception that will trigger an HTTP 405 method not allowed.
-class MethodNotAllowed extends HttpStatusException {
- const MethodNotAllowed(String method) : super(HttpStatus.methodNotAllowed, 'Unsupported method: $method');
-}
-
-/// Exception that will trigger an HTTP 409 conflict.
-class ConflictException extends HttpStatusException {
- const ConflictException([String message = 'Request conflict with server state'])
- : super(HttpStatus.conflict, message);
-}
-
-/// Exception that will trigger an HTTP 500 internal server error.
-class InternalServerError extends HttpStatusException {
- const InternalServerError([String message = 'Internal server error'])
- : super(HttpStatus.internalServerError, message);
-}
-
-/// Exception that will trigger an HTTP 401 not authorized.
-class Unauthorized extends HttpStatusException {
- const Unauthorized([String message = 'Unauthorized']) : super(HttpStatus.unauthorized, message);
-}
-
-/// Exception that will trigger an HTTP 403 forbidden.
-class Forbidden extends HttpStatusException {
- const Forbidden([String message = 'Forbidden']) : super(HttpStatus.forbidden, message);
-}
-
-/// Exception thrown when attempting to authenticate a request that cannot be
-/// authenticated.
-class Unauthenticated implements Exception {
- const Unauthenticated(this.message);
-
- final String message;
-
- @override
- String toString() => 'Unauthenticated: $message';
-}
diff --git a/auto_submit/lib/requests/github_pull_request_event.dart b/auto_submit/lib/requests/github_pull_request_event.dart
deleted file mode 100644
index 00f977c..0000000
--- a/auto_submit/lib/requests/github_pull_request_event.dart
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2023 The Flutter 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 'package:github/github.dart';
-import 'package:json_annotation/json_annotation.dart';
-
-part 'github_pull_request_event.g.dart';
-
-/// PullRequestMessage is a wrapper that keeps the sender and action of the event
-/// sent to the webhook.
-@JsonSerializable()
-class GithubPullRequestEvent {
- const GithubPullRequestEvent({
- this.pullRequest,
- this.action,
- this.sender,
- });
-
- /// The [PullRequest] object information.
- @JsonKey(name: 'pull_request')
- final PullRequest? pullRequest;
-
- /// The action as used by github for a [PullRequest] event.
- final String? action;
-
- /// The author login of the person who initiated the event, currently only
- /// useful when processing revert requests.
- final User? sender;
-
- factory GithubPullRequestEvent.fromJson(Map<String, dynamic> json) => _$GithubPullRequestEventFromJson(json);
-
- Map<String, dynamic> toJson() => _$GithubPullRequestEventToJson(this);
-}
diff --git a/auto_submit/lib/requests/github_pull_request_event.g.dart b/auto_submit/lib/requests/github_pull_request_event.g.dart
deleted file mode 100644
index 7309e50..0000000
--- a/auto_submit/lib/requests/github_pull_request_event.g.dart
+++ /dev/null
@@ -1,20 +0,0 @@
-// GENERATED CODE - DO NOT MODIFY BY HAND
-
-part of 'github_pull_request_event.dart';
-
-// **************************************************************************
-// JsonSerializableGenerator
-// **************************************************************************
-
-GithubPullRequestEvent _$GithubPullRequestEventFromJson(Map<String, dynamic> json) => GithubPullRequestEvent(
- pullRequest:
- json['pull_request'] == null ? null : PullRequest.fromJson(json['pull_request'] as Map<String, dynamic>),
- action: json['action'] as String?,
- sender: json['sender'] == null ? null : User.fromJson(json['sender'] as Map<String, dynamic>),
- );
-
-Map<String, dynamic> _$GithubPullRequestEventToJson(GithubPullRequestEvent instance) => <String, dynamic>{
- 'pull_request': instance.pullRequest,
- 'action': instance.action,
- 'sender': instance.sender,
- };
diff --git a/auto_submit/lib/requests/github_webhook.dart b/auto_submit/lib/requests/github_webhook.dart
deleted file mode 100644
index 90044cb..0000000
--- a/auto_submit/lib/requests/github_webhook.dart
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2022 The Flutter 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:async';
-import 'dart:convert';
-
-import 'package:auto_submit/requests/github_pull_request_event.dart';
-import 'package:github/github.dart';
-import 'package:shelf/shelf.dart';
-import 'package:crypto/crypto.dart';
-
-import '../request_handling/pubsub.dart';
-import '../service/config.dart';
-import '../service/log.dart';
-import '../server/request_handler.dart';
-import '../requests/exceptions.dart';
-
-/// Handler for processing GitHub webhooks.
-///
-/// On events where an 'autosubmit' label was added to a pull request,
-/// check if the pull request is mergable and publish to pubsub.
-class GithubWebhook extends RequestHandler {
- const GithubWebhook({
- required super.config,
- this.pubsub = const PubSub(),
- });
-
- final PubSub pubsub;
-
- static const String pullRequest = 'pull_request';
- static const String labels = 'labels';
- static const String action = 'action';
- static const String sender = 'sender';
-
- static const String eventTypeHeader = 'X-GitHub-Event';
- static const String signatureHeader = 'X-Hub-Signature';
-
- @override
- Future<Response> post(Request request) async {
- final Map<String, String> reqHeader = request.headers;
- log.info('Header: $reqHeader');
-
- final String? gitHubEvent = request.headers[GithubWebhook.eventTypeHeader];
-
- if (gitHubEvent == null || request.headers[GithubWebhook.signatureHeader] == null) {
- throw const BadRequestException('Missing required headers.');
- }
- final List<int> requestBytes = await request.read().expand((_) => _).toList();
- final String? hmacSignature = request.headers[GithubWebhook.signatureHeader];
- if (!await _validateRequest(hmacSignature, requestBytes)) {
- log.info('User is forbidden');
- throw const Forbidden();
- }
-
- bool hasAutosubmit = false;
- bool hasRevertLabel = false;
- final String rawBody = utf8.decode(requestBytes);
- final Map<String, dynamic> body = json.decode(rawBody) as Map<String, dynamic>;
-
- if (!body.containsKey(GithubWebhook.pullRequest) ||
- !((body[GithubWebhook.pullRequest] as Map<String, dynamic>).containsKey(GithubWebhook.labels))) {
- return Response.ok(jsonEncode(<String, String>{}));
- }
-
- final PullRequest pullRequest = PullRequest.fromJson(body[GithubWebhook.pullRequest] as Map<String, dynamic>);
- final String action = body[GithubWebhook.action];
- final User sender = User.fromJson(body[GithubWebhook.sender] as Map<String, dynamic>);
-
- hasAutosubmit = pullRequest.labels!.any((label) => label.name == Config.kAutosubmitLabel);
- hasRevertLabel =
- pullRequest.labels!.any((label) => label.name == Config.kRevertLabel || label.name == Config.kRevertOfLabel);
-
- // Check for revert label first.
- if (hasRevertLabel) {
- log.info('Found pull request with the revert label.');
- await pubsub.publish(
- config.pubsubRevertRequestTopic,
- GithubPullRequestEvent(
- pullRequest: pullRequest,
- action: action,
- sender: sender,
- ),
- );
- } else if (hasAutosubmit) {
- log.info('Found pull request with autosubmit label.');
- await pubsub.publish(config.pubsubPullRequestTopic, pullRequest);
- }
-
- return Response.ok(rawBody);
- }
-
- Future<bool> _validateRequest(
- String? signature,
- List<int> requestBody,
- ) async {
- final String rawKey = await config.getWebhookKey();
- final List<int> key = utf8.encode(rawKey);
- final Hmac hmac = Hmac(sha1, key);
- final Digest digest = hmac.convert(requestBody);
- final String bodySignature = 'sha1=$digest';
- return bodySignature == signature;
- }
-}
diff --git a/auto_submit/lib/requests/graphql_queries.dart b/auto_submit/lib/requests/graphql_queries.dart
deleted file mode 100644
index 0e7203d..0000000
--- a/auto_submit/lib/requests/graphql_queries.dart
+++ /dev/null
@@ -1,187 +0,0 @@
-// Copyright 2023 The Flutter 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 'package:gql/ast.dart';
-import 'package:gql/language.dart' as lang;
-
-/// Provides a way to encapsulate the named variables that will be needed for
-/// each request made via the graphql api.
-abstract class GraphQLOperation {
- /// The list of variables that will be injected into the partner
- /// [DocumentNode] made in the graphql service.
- Map<String, dynamic> get variables;
-
- /// The document that contains the GraphQL operation.
- DocumentNode get documentNode;
-}
-
-/// [FindPullRequestsWithReviewsQuery] encapsulates the input variables and
-/// [DocumentNode] needed to get a pull request with the last 30 reviews.
-class FindPullRequestsWithReviewsQuery extends GraphQLOperation {
- FindPullRequestsWithReviewsQuery({
- required this.repositoryOwner,
- required this.repositoryName,
- required this.pullRequestNumber,
- });
-
- final String repositoryOwner;
- final String repositoryName;
- final int pullRequestNumber;
-
- @override
- Map<String, dynamic> get variables => <String, dynamic>{
- 'sOwner': repositoryOwner,
- 'sName': repositoryName,
- 'sPrNumber': pullRequestNumber,
- };
-
- @override
- DocumentNode get documentNode => lang.parseString(r'''
-query LabeledPullRequestWithReviews($sOwner: String!, $sName: String!, $sPrNumber: Int!) {
- repository(owner: $sOwner, name: $sName) {
- pullRequest(number: $sPrNumber) {
- author {
- login
- }
- authorAssociation
- id
- title
- mergeable
- commits(last:1) {
- nodes {
- commit {
- abbreviatedOid
- oid
- committedDate
- pushedDate
- status {
- contexts {
- context
- state
- targetUrl
- }
- }
- }
- }
- }
- reviews(last: 30, states: [APPROVED, CHANGES_REQUESTED]) {
- nodes {
- author {
- login
- }
- authorAssociation
- state
- }
- }
- }
- }
-}''');
-}
-
-/// [FindPullRequestNodeIdQuery] encapsulate the input variables and
-/// [DocumentNode] needed to the query the Pull Request Node ID that github uses
-/// to locate a pull request accross all repos.
-class FindPullRequestNodeIdQuery extends GraphQLOperation {
- FindPullRequestNodeIdQuery({
- required this.repositoryOwner,
- required this.repositoryName,
- required this.pullRequestNumber,
- });
-
- final String repositoryOwner;
- final String repositoryName;
- final int pullRequestNumber;
-
- @override
- Map<String, dynamic> get variables => {
- 'repoOwner': repositoryOwner,
- 'repoName': repositoryName,
- 'pullRequestNumber': pullRequestNumber,
- };
-
- @override
- DocumentNode get documentNode => lang.parseString(r'''
-query FindPullRequestNodeId ($repoOwner:String!, $repoName:String!, $pullRequestNumber:Int!) {
- repository(owner:$repoOwner, name:$repoName) {
- pullRequest(number:$pullRequestNumber) {
- id
- }
- }
-}
-''');
-}
-
-/// [RevertPullRequestMutation] encapsulates the input variables and
-/// [DocumentNode] needed to perform the revert request mutation to revert a
-/// closed pull request.
-class RevertPullRequestMutation extends GraphQLOperation {
- RevertPullRequestMutation(
- this.body,
- this.clientMutationId,
- this.draft,
- this.id,
- this.title,
- );
-
- final String body;
- final String? clientMutationId;
- final bool draft;
- final String id;
- final String title;
-
- @override
- Map<String, dynamic> get variables => {
- 'revertBody': body,
- 'clientMutationId': clientMutationId,
- 'draft': draft,
- 'pullRequestId': id,
- 'revertTitle': title,
- };
-
- @override
- DocumentNode get documentNode => lang.parseString(r'''
-mutation RevertPullFlutterPullRequest ($revertBody:String!, $clientMutationId:String!, $draft:Boolean, $pullRequestId:ID!, $revertTitle:String!) {
- revertPullRequest (
- input: {
- body:$revertBody,
- clientMutationId: $clientMutationId,
- draft: $draft,
- pullRequestId: $pullRequestId,
- title: $revertTitle
- }) {
- clientMutationId
- pullRequest {
- author {
- login
- }
- authorAssociation
- id
- title
- number
- repository {
- owner {
- login
- }
- name
- }
- }
- revertPullRequest {
- author {
- login
- }
- authorAssociation
- id
- title
- number
- repository {
- owner {
- login
- }
- name
- }
- }
- }
-}
-''');
-}
diff --git a/auto_submit/lib/requests/readiness_check.dart b/auto_submit/lib/requests/readiness_check.dart
deleted file mode 100644
index 428f9a6..0000000
--- a/auto_submit/lib/requests/readiness_check.dart
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2022 The Flutter 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:async';
-
-import 'package:shelf/shelf.dart';
-
-import '../server/request_handler.dart';
-
-/// Handler for readiness checks.
-class ReadinessCheck extends RequestHandler {
- const ReadinessCheck({
- required super.config,
- });
-
- @override
- Future<Response> get() async {
- return Response.ok('OK');
- }
-
- @override
- Future<Response> run(Request request) async {
- return super.run(request);
- }
-}
diff --git a/auto_submit/lib/server/authenticated_request_handler.dart b/auto_submit/lib/server/authenticated_request_handler.dart
deleted file mode 100644
index c62f9ad..0000000
--- a/auto_submit/lib/server/authenticated_request_handler.dart
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:meta/meta.dart';
-import 'package:shelf/shelf.dart';
-
-import 'request_handler.dart';
-import '../request_handling/authentication.dart';
-import '../requests/exceptions.dart';
-import '../service/log.dart';
-
-/// A [RequestHandler] that handles API requests.
-///
-/// * All requests must be authenticated per [CronAuthProvider].
-@immutable
-abstract class AuthenticatedRequestHandler extends RequestHandler {
- /// Creates a new [ApiRequestHandler].
- const AuthenticatedRequestHandler({
- required super.config,
- required this.cronAuthProvider,
- });
-
- /// Service responsible for authenticating this [Request].
- final CronAuthProvider cronAuthProvider;
-
- @override
- Future<Response> run(Request request) async {
- try {
- await cronAuthProvider.authenticate(request);
- } on Unauthenticated catch (error) {
- log.info('Authenticate error: $error');
- return Response.forbidden(error.toString());
- }
- return super.run(request);
- }
-}
diff --git a/auto_submit/lib/server/request_handler.dart b/auto_submit/lib/server/request_handler.dart
deleted file mode 100644
index 8d2499d..0000000
--- a/auto_submit/lib/server/request_handler.dart
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-
-import 'package:meta/meta.dart';
-import 'package:shelf/shelf.dart';
-
-import '../service/config.dart';
-import '../requests/exceptions.dart';
-
-@immutable
-abstract class RequestHandler {
- const RequestHandler({
- required this.config,
- });
-
- final Config config;
-
- /// Services a request.
- ///
- /// The default implementation will respond with 405 method not allowed.
- @protected
- Future<Response> run(Request request) async {
- try {
- switch (request.method) {
- case 'GET':
- return await get() as Response;
- case 'POST':
- return await post(request) as Response;
- default:
- throw MethodNotAllowed(request.method);
- }
- } on HttpStatusException {
- rethrow;
- }
- }
-
- /// Services a GET request.
- ///
- /// Subclasses should override this method if they support GET requests.
- /// The default implementation will respond with 405 method not allowed.
- @protected
- Future get() async {
- throw const MethodNotAllowed('GET');
- }
-
- /// Services a POST request.
- ///
- /// Subclasses should override this method if they support POST requests.
- /// The default implementation will respond with 405 method not allowed.
- @protected
- Future post(Request request) async {
- throw const MethodNotAllowed('POST');
- }
-}
diff --git a/auto_submit/lib/service/access_client_provider.dart b/auto_submit/lib/service/access_client_provider.dart
deleted file mode 100644
index eb60463..0000000
--- a/auto_submit/lib/service/access_client_provider.dart
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2022 The Flutter 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 'package:googleapis_auth/auth_io.dart';
-import 'package:http/http.dart';
-
-class AccessClientProvider {
- /// Returns an OAuth 2.0 authenticated access client for the device lab service account.
- Future<Client> createAccessClient({
- List<String> scopes = const <String>['https://www.googleapis.com/auth/cloud-platform'],
- }) async {
- return clientViaApplicationDefaultCredentials(scopes: scopes);
- }
-}
diff --git a/auto_submit/lib/service/approver_service.dart b/auto_submit/lib/service/approver_service.dart
deleted file mode 100644
index 22ba637..0000000
--- a/auto_submit/lib/service/approver_service.dart
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2022 The Flutter 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 'package:auto_submit/service/log.dart';
-import 'package:github/github.dart' as github;
-
-import '../configuration/repository_configuration.dart';
-import '../service/config.dart';
-
-/// Function signature for a [ApproverService] provider.
-typedef ApproverServiceProvider = ApproverService Function(Config config);
-
-/// Provides github PR approval services.
-class ApproverService {
- const ApproverService(this.config);
-
- final Config config;
-
- /// Creates and returns a [ApproverService] using [config].
- static ApproverService defaultProvider(Config config) {
- return ApproverService(config);
- }
-
- /// Get the auto approval accounts from the configuration is any are supplied.
- Future<Set<String>> getAutoApprovalAccounts(github.RepositorySlug slug) async {
- final RepositoryConfiguration repositoryConfiguration = await config.getRepositoryConfiguration(slug);
- final Set<String> approvalAccounts = repositoryConfiguration.autoApprovalAccounts;
- return approvalAccounts;
- }
-
- Future<void> autoApproval(github.PullRequest pullRequest) async {
- final String? author = pullRequest.user!.login;
- final int prNumber = pullRequest.number!;
- final github.RepositorySlug slug = pullRequest.base!.repo!.slug();
- final Set<String> approvalAccounts =
- await getAutoApprovalAccounts(github.RepositorySlug.full(pullRequest.base!.repo!.fullName));
-
- log.info('Determining auto approval of $author on ${slug.fullName}/$prNumber.');
-
- // If there are auto_approvers let them approve the pull request.
- if (!approvalAccounts.contains(author)) {
- log.info('Auto-review ignored for $author on ${slug.fullName}/$prNumber.');
- } else {
- log.info('Auto approval detected on ${slug.fullName}/$prNumber.');
- await _approve(pullRequest, author);
- }
- }
-
- Future<void> _approve(github.PullRequest pullRequest, String? author) async {
- final github.RepositorySlug slug = pullRequest.base!.repo!.slug();
- final github.GitHub botClient = await config.createFlutterGitHubBotClient(slug);
-
- final Stream<github.PullRequestReview> reviews = botClient.pullRequests.listReviews(slug, pullRequest.number!);
- // TODO(ricardoamador) this will need to be refactored to make this code more general and
- // not applicable to only flutter.
- await for (github.PullRequestReview review in reviews) {
- if (review.user.login == 'fluttergithubbot' && review.state == 'APPROVED') {
- // Already approved.
- return;
- }
- }
-
- final github.CreatePullRequestReview review =
- github.CreatePullRequestReview(slug.owner, slug.name, pullRequest.number!, 'APPROVE');
- await botClient.pullRequests.createReview(slug, review);
- log.info('Review for ${slug.fullName}/${pullRequest.number} complete');
- }
-}
diff --git a/auto_submit/lib/service/bigquery.dart b/auto_submit/lib/service/bigquery.dart
deleted file mode 100644
index a174b2b..0000000
--- a/auto_submit/lib/service/bigquery.dart
+++ /dev/null
@@ -1,410 +0,0 @@
-// Copyright 2022 The Flutter 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:async';
-
-import 'package:auto_submit/exception/bigquery_exception.dart';
-import 'package:auto_submit/model/big_query_pull_request_record.dart';
-import 'package:googleapis/bigquery/v2.dart';
-import 'package:http/http.dart';
-
-import 'access_client_provider.dart';
-
-const String selectRevertRequestDml = r'''
-SELECT organization,
- repository,
- reverting_pr_author,
- reverting_pr_number,
- reverting_pr_commit,
- reverting_pr_created_timestamp,
- reverting_pr_landed_timestamp,
- original_pr_author,
- original_pr_number,
- original_pr_commit,
- original_pr_created_timestamp,
- original_pr_landed_timestamp,
- review_issue_assignee,
- review_issue_number,
- review_issue_created_timestamp,
- review_issue_landed_timestamp,
- review_issue_closed_by
-FROM `flutter-dashboard.revert.revert_requests`
-WHERE reverting_pr_number=@REVERTING_PR_NUMBER AND repository=@REPOSITORY
-''';
-
-/// Query to select all of the open revert review issues for update. Note that
-/// the review_issue_landed_timestamp is 0 for open issues. 0 is the default
-/// value instead of null since it is safer to process.
-const String selectRevertRequestReviewIssuesDml = r'''
-SELECT review_issue_assignee,
- review_issue_number,
- review_issue_created_timestamp,
- review_issue_landed_timestamp,
- review_issue_closed_by
-FROM `flutter-dashboard.revert.revert_requests`
-WHERE review_issue_landed_timestamp=0
-''';
-
-const String updateRevertRequestRecordReviewDml = '''
-UPDATE `flutter-dashboard.revert.revert_requests`
-SET review_issue_landed_timestamp=@REVIEW_ISSUE_LANDED_TIMESTAMP,
- review_issue_closed_by=@REVIEW_ISSUE_CLOSED_BY
-WHERE review_issue_number=@REVIEW_ISSUE_NUMBER
-''';
-
-const String insertRevertRequestDml = r'''
-INSERT INTO `flutter-dashboard.revert.revert_requests` (
- organization,
- repository,
- reverting_pr_author,
- reverting_pr_number,
- reverting_pr_commit,
- reverting_pr_created_timestamp,
- reverting_pr_landed_timestamp,
- original_pr_author,
- original_pr_number,
- original_pr_commit,
- original_pr_created_timestamp,
- original_pr_landed_timestamp,
- review_issue_assignee,
- review_issue_number,
- review_issue_created_timestamp,
- review_issue_landed_timestamp,
- review_issue_closed_by
-) VALUES (
- @ORGANIZATION,
- @REPOSITORY,
- @REVERTING_PR_AUTHOR,
- @REVERTING_PR_NUMBER,
- @REVERTING_PR_COMMIT,
- @REVERTING_PR_CREATED_TIMESTAMP,
- @REVERTING_PR_LANDED_TIMESTAMP,
- @ORIGINAL_PR_AUTHOR,
- @ORIGINAL_PR_NUMBER,
- @ORIGINAL_PR_COMMIT,
- @ORIGINAL_PR_CREATED_TIMESTAMP,
- @ORIGINAL_PR_LANDED_TIMESTAMP,
- @REVIEW_ISSUE_ASSIGNEE,
- @REVIEW_ISSUE_NUMBER,
- @REVIEW_ISSUE_CREATED_TIMESTAMP,
- @REVIEW_ISSUE_LANDED_TIMESTAMP,
- @REVIEW_ISSUE_CLOSED_BY
-)
-''';
-
-const String deleteRevertRequestDml = r'''
-DELETE FROM `flutter-dashboard.revert.revert_requests`
-WHERE reverting_pr_number=@REVERTING_PR_NUMBER AND repository=@REPOSITORY
-''';
-
-const String insertPullRequestDml = r'''
-INSERT INTO `flutter-dashboard.autosubmit.pull_requests` (
- pr_created_timestamp,
- pr_landed_timestamp,
- organization,
- repository,
- author,
- pr_number,
- pr_commit,
- pr_request_type
-) VALUES (
- @PR_CREATED_TIMESTAMP,
- @PR_LANDED_TIMESTAMP,
- @ORGANIZATION,
- @REPOSITORY,
- @AUTHOR,
- @PR_NUMBER,
- @PR_COMMIT,
- @PR_REQUEST_TYPE
-)
-''';
-
-const String selectPullRequestDml = r'''
-SELECT pr_created_timestamp,
- pr_landed_timestamp,
- organization,
- repository,
- author,
- pr_number,
- pr_commit,
- pr_request_type
-FROM `flutter-dashboard.autosubmit.pull_requests`
-WHERE pr_number=@PR_NUMBER AND repository=@REPOSITORY
-''';
-
-const String deletePullRequestDml = r'''
-DELETE FROM `flutter-dashboard.autosubmit.pull_requests`
-WHERE pr_number=@PR_NUMBER AND repository=@REPOSITORY
-''';
-
-class BigqueryService {
- const BigqueryService(this.accessClientProvider);
-
- /// AccessClientProvider for OAuth 2.0 authenticated access client
- final AccessClientProvider accessClientProvider;
-
- /// Return a [TabledataResource] with an authenticated [client]
- Future<TabledataResource> defaultTabledata() async {
- final Client client = await accessClientProvider.createAccessClient(
- scopes: const <String>[BigqueryApi.bigqueryScope],
- );
- return BigqueryApi(client).tabledata;
- }
-
- /// Return a [JobsResource] with an authenticated [client]
- Future<JobsResource> defaultJobs() async {
- final Client client = await accessClientProvider.createAccessClient(
- scopes: const <String>[BigqueryApi.bigqueryScope],
- );
- return BigqueryApi(client).jobs;
- }
-
- /// Query the database to update a revert review issue with the timestamp the
- /// issue was closed at and the person who closed the issue.
- Future<void> updateReviewRequestIssue({
- required String projectId,
- required DateTime reviewIssueLandedTimestamp,
- required int reviewIssueNumber,
- required String reviewIssueClosedBy,
- }) async {
- final JobsResource jobsResource = await defaultJobs();
-
- final QueryRequest queryRequest = QueryRequest(
- query: updateRevertRequestRecordReviewDml,
- queryParameters: <QueryParameter>[
- _createIntegerQueryParameter(
- 'REVIEW_ISSUE_LANDED_TIMESTAMP',
- reviewIssueLandedTimestamp.millisecondsSinceEpoch,
- ),
- _createIntegerQueryParameter(
- 'REVIEW_ISSUE_NUMBER',
- reviewIssueNumber,
- ),
- _createStringQueryParameter(
- 'REVIEW_ISSUE_CLOSED_BY',
- reviewIssueClosedBy,
- ),
- ],
- useLegacySql: false,
- );
-
- final QueryResponse queryResponse = await jobsResource.query(queryRequest, projectId);
- if (!queryResponse.jobComplete!) {
- throw BigQueryException(
- 'Update of review issue $reviewIssueNumber did not complete.',
- );
- }
-
- if (queryResponse.numDmlAffectedRows != null && int.parse(queryResponse.numDmlAffectedRows!) != 1) {
- throw BigQueryException(
- 'There was an error updating revert request record review issue landed timestamp with review issue number $reviewIssueNumber.',
- );
- }
- }
-
- Future<void> deleteRevertRequestRecord({
- required String projectId,
- required int prNumber,
- required String repository,
- }) async {
- final JobsResource jobsResource = await defaultJobs();
-
- final QueryRequest queryRequest = QueryRequest(
- query: deleteRevertRequestDml,
- queryParameters: <QueryParameter>[
- _createIntegerQueryParameter('REVERTING_PR_NUMBER', prNumber),
- _createStringQueryParameter('REPOSITORY', repository),
- ],
- useLegacySql: false,
- );
-
- final QueryResponse queryResponse = await jobsResource.query(queryRequest, projectId);
- if (!queryResponse.jobComplete!) {
- throw BigQueryException(
- 'Delete revert request with pr# $prNumber in repository $repository did not complete.',
- );
- }
-
- if (queryResponse.numDmlAffectedRows == null || int.parse(queryResponse.numDmlAffectedRows!) == 0) {
- throw BigQueryException(
- 'Could not find revert request with pr# $prNumber in repository $repository to delete.',
- );
- }
-
- if (int.parse(queryResponse.numDmlAffectedRows!) != 1) {
- throw BigQueryException(
- 'More than one row was deleted from the database for revert request with pr# $prNumber in repository $repository.',
- );
- }
- }
-
- /// Insert a new pull request record into the database.
- Future<void> insertPullRequestRecord({
- required String projectId,
- required PullRequestRecord pullRequestRecord,
- }) async {
- final JobsResource jobsResource = await defaultJobs();
-
- final QueryRequest queryRequest = QueryRequest(
- query: insertPullRequestDml,
- queryParameters: <QueryParameter>[
- _createIntegerQueryParameter(
- 'PR_CREATED_TIMESTAMP',
- pullRequestRecord.prCreatedTimestamp!.millisecondsSinceEpoch,
- ),
- _createIntegerQueryParameter(
- 'PR_LANDED_TIMESTAMP',
- pullRequestRecord.prLandedTimestamp!.millisecondsSinceEpoch,
- ),
- _createStringQueryParameter(
- 'ORGANIZATION',
- pullRequestRecord.organization,
- ),
- _createStringQueryParameter(
- 'REPOSITORY',
- pullRequestRecord.repository,
- ),
- _createStringQueryParameter(
- 'AUTHOR',
- pullRequestRecord.author,
- ),
- _createIntegerQueryParameter(
- 'PR_NUMBER',
- pullRequestRecord.prNumber,
- ),
- _createStringQueryParameter(
- 'PR_COMMIT',
- pullRequestRecord.prCommit,
- ),
- _createStringQueryParameter(
- 'PR_REQUEST_TYPE',
- pullRequestRecord.prRequestType,
- ),
- ],
- useLegacySql: false,
- );
-
- final QueryResponse queryResponse = await jobsResource.query(queryRequest, projectId);
- if (!queryResponse.jobComplete!) {
- throw BigQueryException(
- 'Insert pull request $pullRequestRecord did not complete.',
- );
- }
-
- if (queryResponse.numDmlAffectedRows != null && int.parse(queryResponse.numDmlAffectedRows!) != 1) {
- throw BigQueryException(
- 'There was an error inserting $pullRequestRecord into the table.',
- );
- }
- }
-
- /// Select a specific pull request form the database.
- Future<PullRequestRecord> selectPullRequestRecordByPrNumber({
- required String projectId,
- required int prNumber,
- required String repository,
- }) async {
- final JobsResource jobsResource = await defaultJobs();
-
- final QueryRequest queryRequest = QueryRequest(
- query: selectPullRequestDml,
- queryParameters: <QueryParameter>[
- _createIntegerQueryParameter('PR_NUMBER', prNumber),
- _createStringQueryParameter('REPOSITORY', repository),
- ],
- useLegacySql: false,
- );
-
- final QueryResponse queryResponse = await jobsResource.query(queryRequest, projectId);
- if (!queryResponse.jobComplete!) {
- throw BigQueryException(
- 'Get pull request by pr# $prNumber in repository $repository did not complete.',
- );
- }
-
- final List<TableRow>? tableRows = queryResponse.rows;
- if (tableRows == null || tableRows.isEmpty) {
- throw BigQueryException(
- 'Could not find an entry for pull request with pr# $prNumber in repository $repository.',
- );
- }
-
- if (tableRows.length != 1) {
- throw BigQueryException(
- 'More than one record was returned for pull request with pr# $prNumber in repository $repository.',
- );
- }
-
- final TableRow tableRow = tableRows.first;
-
- return PullRequestRecord(
- prCreatedTimestamp: (tableRow.f![0].v != null)
- ? DateTime.fromMillisecondsSinceEpoch(int.parse(tableRow.f![0].v as String))
- : null,
- prLandedTimestamp: (tableRow.f![1].v != null)
- ? DateTime.fromMillisecondsSinceEpoch(int.parse(tableRow.f![1].v as String))
- : null,
- organization: tableRow.f![2].v as String,
- repository: tableRow.f![3].v as String,
- author: tableRow.f![4].v as String,
- prNumber: int.parse(tableRow.f![5].v as String),
- prCommit: tableRow.f![6].v as String,
- prRequestType: tableRow.f![7].v as String,
- );
- }
-
- Future<void> deletePullRequestRecord({
- required String projectId,
- required int prNumber,
- required String repository,
- }) async {
- final JobsResource jobsResource = await defaultJobs();
-
- final QueryRequest queryRequest = QueryRequest(
- query: deletePullRequestDml,
- queryParameters: <QueryParameter>[
- _createIntegerQueryParameter('PR_NUMBER', prNumber),
- _createStringQueryParameter('REPOSITORY', repository),
- ],
- useLegacySql: false,
- );
-
- final QueryResponse queryResponse = await jobsResource.query(queryRequest, projectId);
- if (!queryResponse.jobComplete!) {
- throw BigQueryException(
- 'Delete pull request with pr# $prNumber in repository $repository did not complete.',
- );
- }
-
- if (queryResponse.numDmlAffectedRows == null || int.parse(queryResponse.numDmlAffectedRows!) == 0) {
- throw BigQueryException(
- 'Could not find pull request with pr# $prNumber in repository $repository to delete.',
- );
- }
-
- if (int.parse(queryResponse.numDmlAffectedRows!) != 1) {
- throw BigQueryException(
- 'More than one row was deleted from the database for pull request with pr# $prNumber in repository $repository.',
- );
- }
- }
-
- /// Create an int parameter for query substitution.
- QueryParameter _createIntegerQueryParameter(String name, int? value) {
- return QueryParameter(
- name: name,
- parameterType: QueryParameterType(type: 'INT64'),
- parameterValue: QueryParameterValue(value: value.toString()),
- );
- }
-
- /// Create a String parameter for query substitution.
- QueryParameter _createStringQueryParameter(String name, String? value) {
- return QueryParameter(
- name: name,
- parameterType: QueryParameterType(type: 'STRING'),
- parameterValue: QueryParameterValue(value: value),
- );
- }
-}
diff --git a/auto_submit/lib/service/config.dart b/auto_submit/lib/service/config.dart
deleted file mode 100644
index c1c0d96..0000000
--- a/auto_submit/lib/service/config.dart
+++ /dev/null
@@ -1,284 +0,0 @@
-// Copyright 2022 The Flutter 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:convert';
-import 'dart:typed_data';
-
-import 'package:auto_submit/configuration/repository_configuration.dart';
-import 'package:auto_submit/configuration/repository_configuration_manager.dart';
-import 'package:corsac_jwt/corsac_jwt.dart';
-import 'package:github/github.dart';
-import 'package:googleapis/bigquery/v2.dart';
-import 'package:graphql/client.dart';
-import 'package:http/http.dart' as http;
-import 'package:neat_cache/cache_provider.dart';
-import 'package:neat_cache/neat_cache.dart';
-import 'package:retry/retry.dart';
-
-import '../foundation/providers.dart';
-import '../service/secrets.dart';
-import 'access_client_provider.dart';
-import 'bigquery.dart';
-import 'github_service.dart';
-import 'log.dart';
-
-/// Configuration for the autosubmit engine.
-class Config {
- Config({
- required this.cacheProvider,
- this.httpProvider = Providers.freshHttpClient,
- required this.secretManager,
- }) {
- repositoryConfigurationManager = RepositoryConfigurationManager(this, cache);
- }
-
- late RepositoryConfigurationManager repositoryConfigurationManager;
-
- /// Project/GCP constants
- static const String flutter = 'flutter';
- static const String flutterGcpProjectId = 'flutter-dashboard';
-
- // List of environment variable keys related to the Github app authentication.
- static const String kGithubKey = 'AUTO_SUBMIT_GITHUB_KEY';
- static const String kGithubAppId = 'AUTO_SUBMIT_GITHUB_APP_ID';
- static const String kWebHookKey = 'AUTO_SUBMIT_WEBHOOK_TOKEN';
- static const String kFlutterGitHubBotKey = 'AUTO_SUBMIT_FLUTTER_GITHUB_TOKEN';
-
- /// Labels autosubmit looks for on pull requests
- static const String kAutosubmitLabel = 'autosubmit';
-
- // Labels the bot looks for on revert requests.
- // TODO (ricardoamador) https://github.com/flutter/flutter/issues/134845:
- // add a link to a one page doc outlining the workflow that happens here.
-
- /// The `revert` label is used by developers to initiate the revert request.
- /// This signals to the service that it should revert the changes in this pull
- /// request.
- static const String kRevertLabel = 'revert';
-
- /// The `revert of` label is used exclusively by the bot. The user does not
- /// add this. When the bot successfully pushes the revert request to Github
- /// it adds this label to signify that it should then validate and merge this
- /// as a revert.
- static const String kRevertOfLabel = 'revert of';
-
- /// The label which shows the overrideTree Status.
- String get overrideTreeStatusLabel => 'warning: land on red to fix tree breakage';
-
- /// Repository Slug data
- /// GitHub repositories that use CI status to determine if pull requests can be submitted.
- static Set<RepositorySlug> reposWithTreeStatus = <RepositorySlug>{
- engineSlug,
- flutterSlug,
- };
- static RepositorySlug get engineSlug => RepositorySlug('flutter', 'engine');
- static RepositorySlug get flutterSlug => RepositorySlug('flutter', 'flutter');
-
- String get autosubmitBot => 'auto-submit[bot]';
-
- /// The names of autoroller accounts for the repositories.
- ///
- /// These accounts should not need reviews before merging. See
- /// https://github.com/flutter/flutter/wiki/Autorollers
- Set<String> get rollerAccounts => const <String>{
- 'skia-flutter-autoroll',
- 'engine-flutter-autoroll',
- // REST API returns dependabot[bot] as author while GraphQL returns dependabot. We need
- // both as we use graphQL to merge the PR and REST API to approve the PR.
- 'dependabot[bot]',
- 'dependabot',
- 'DartDevtoolWorkflowBot',
- };
-
- /// Repository configuration variables
- Duration get repositoryConfigurationTtl => const Duration(minutes: 10);
-
- /// PubSub configs
- int get kPullMesssageBatchSize => 100;
-
- /// Number of Pub/Sub pull calls in each cron job run.
- ///
- /// TODO(keyonghan): monitor and optimize this number based on response time
- /// https://github.com/flutter/cocoon/pull/2035/files#r938143840.
- int get kPubsubPullNumber => 5;
-
- static String get pubsubTopicsPrefix => 'projects/$flutterGcpProjectId/topics';
- static String get pubsubSubscriptionsPrefix => 'projects/$flutterGcpProjectId/subscriptions';
-
- String get pubsubPullRequestTopic => 'auto-submit-queue';
- String get pubsubPullRequestSubscription => 'auto-submit-queue-sub';
-
- String get pubsubRevertRequestTopic => 'auto-submit-revert-queue';
- String get pubsubRevertRequestSubscription => 'auto-submit-revert-queue-sub';
-
- /// Retry options for timing related retryable code.
- static const RetryOptions mergeRetryOptions = RetryOptions(
- delayFactor: Duration(milliseconds: 200),
- maxDelay: Duration(seconds: 1),
- maxAttempts: 5,
- );
-
- static const RetryOptions requiredChecksRetryOptions = RetryOptions(
- delayFactor: Duration(milliseconds: 500),
- maxDelay: Duration(seconds: 5),
- maxAttempts: 5,
- );
-
- /// Pull request approval message
- static const String pullRequestApprovalRequirementsMessage =
- '- Merge guidelines: You need at least one approved review if you are already '
- 'part of flutter-hackers or two member reviews if you are not a flutter-hacker '
- 'before re-applying the autosubmit label. __Reviewers__: If you left a comment '
- 'approving, please use the "approve" review action instead.';
-
- /// Config object members
- final CacheProvider cacheProvider;
- final HttpProvider httpProvider;
- final SecretManager secretManager;
-
- Cache get cache => Cache<dynamic>(cacheProvider).withPrefix('config');
-
- Future<RepositoryConfiguration> getRepositoryConfiguration(RepositorySlug slug) async {
- return repositoryConfigurationManager.readRepositoryConfiguration(slug);
- }
-
- Future<GithubService> createGithubService(RepositorySlug slug) async {
- final GitHub github = await createGithubClient(slug);
- return GithubService(github);
- }
-
- Future<GitHub> createGithubClient(RepositorySlug slug) async {
- final String token = await generateGithubToken(slug);
- return GitHub(auth: Authentication.withToken(token));
- }
-
- Future<GitHub> createFlutterGitHubBotClient(RepositorySlug slug) async {
- final String token = await getFlutterGitHubBotToken();
- return GitHub(auth: Authentication.withToken(token));
- }
-
- Future<String> generateGithubToken(RepositorySlug slug) async {
- // GitHub's secondary rate limits are run into very frequently when making auth tokens.
- final Uint8List? cacheValue = await cache['githubToken-${slug.owner}'].get(
- () => _generateGithubToken(slug),
- // Tokens have a TTL of 10 minutes. AppEngine requests have a TTL of 1 minute.
- // To ensure no expired tokens are used, set this to 10 - 1, with an extra buffer of a duplicate request.
- const Duration(minutes: 8),
- ) as Uint8List?;
- return String.fromCharCodes(cacheValue!);
- }
-
- Future<String> getInstallationId(RepositorySlug slug) async {
- final String jwt = await _generateGithubJwt();
- final Map<String, String> headers = <String, String>{
- 'Authorization': 'Bearer $jwt',
- 'Accept': 'application/vnd.github.machine-man-preview+json',
- };
- // TODO(KristinBi): Upstream the github package.https://github.com/flutter/flutter/issues/100920
- final Uri githubInstallationUri = Uri.https('api.github.com', 'app/installations');
- final http.Client client = httpProvider();
- // TODO(KristinBi): Track the installation id by repo. https://github.com/flutter/flutter/issues/100808
- final http.Response response = await client.get(
- githubInstallationUri,
- headers: headers,
- );
- final List<Map<String, dynamic>> list = (json.decode(response.body) as List<dynamic>).cast<Map<String, dynamic>>();
- late String installationId;
- for (Map<String, dynamic> installData in list) {
- if (installData['account']!['login']!.toString() == slug.owner) {
- installationId = installData['id']!.toString();
- }
- }
- return installationId;
- }
-
- Future<GraphQLClient> createGitHubGraphQLClient(RepositorySlug slug) async {
- final HttpLink httpLink = HttpLink(
- 'https://api.github.com/graphql',
- defaultHeaders: <String, String>{
- 'Accept': 'application/vnd.github.antiope-preview+json',
- },
- );
-
- final String token = await generateGithubToken(slug);
-
- final AuthLink authLink = AuthLink(
- getToken: () async => 'Bearer $token',
- );
-
- return GraphQLClient(
- cache: GraphQLCache(),
- link: authLink.concat(httpLink),
- );
- }
-
- Future<BigqueryService> createBigQueryService() async {
- final AccessClientProvider accessClientProvider = AccessClientProvider();
- return BigqueryService(accessClientProvider);
- }
-
- Future<TabledataResource> createTabledataResourceApi() async {
- return (await createBigQueryService()).defaultTabledata();
- }
-
- Future<Uint8List> _generateGithubToken(RepositorySlug slug) async {
- final String jwt = await _generateGithubJwt();
- final Map<String, String> headers = <String, String>{
- 'Authorization': 'Bearer $jwt',
- 'Accept': 'application/vnd.github.machine-man-preview+json',
- };
- final String installationId = await getInstallationId(slug);
- final Uri githubAccessTokensUri = Uri.https('api.github.com', 'app/installations/$installationId/access_tokens');
- final http.Client client = httpProvider();
- final http.Response response = await client.post(
- githubAccessTokensUri,
- headers: headers,
- );
- final Map<String, dynamic> jsonBody = jsonDecode(response.body) as Map<String, dynamic>;
- if (jsonBody.containsKey('token') == false) {
- log.warning(response.body);
- throw Exception('generateGithubToken failed to get token from Github');
- }
- final String token = jsonBody['token'] as String;
- return Uint8List.fromList(token.codeUnits);
- }
-
- Future<String> _generateGithubJwt() async {
- final String rawKey = await secretManager.get(kGithubKey);
- final StringBuffer sb = StringBuffer();
- sb.writeln(rawKey.substring(0, 32));
- sb.writeln(rawKey.substring(32, rawKey.length - 30).replaceAll(' ', ' \n'));
- sb.writeln(rawKey.substring(rawKey.length - 30, rawKey.length));
- final String privateKey = sb.toString();
- final JWTBuilder builder = JWTBuilder();
- final DateTime now = DateTime.now();
- builder
- ..issuer = await secretManager.get(kGithubAppId)
- ..issuedAt = now
- ..expiresAt = now.add(const Duration(minutes: 10));
- final JWTRsaSha256Signer signer = JWTRsaSha256Signer(privateKey: privateKey);
- final JWT signedToken = builder.getSignedToken(signer);
- return signedToken.toString();
- }
-
- /// Get the webhook key
- Future<String> getWebhookKey() async {
- final Uint8List? cacheValue = await cache[kWebHookKey].get(
- () => _getValueFromSecretManager(kWebHookKey),
- ) as Uint8List?;
- return String.fromCharCodes(cacheValue!);
- }
-
- Future<String> getFlutterGitHubBotToken() async {
- final Uint8List? cacheValue = await cache[kFlutterGitHubBotKey].get(
- () => _getValueFromSecretManager(kFlutterGitHubBotKey),
- ) as Uint8List?;
- return String.fromCharCodes(cacheValue!);
- }
-
- Future<Uint8List> _getValueFromSecretManager(String key) async {
- final String value = await secretManager.get(key);
- return Uint8List.fromList(value.codeUnits);
- }
-}
diff --git a/auto_submit/lib/service/github_service.dart b/auto_submit/lib/service/github_service.dart
deleted file mode 100644
index ef2f5a6..0000000
--- a/auto_submit/lib/service/github_service.dart
+++ /dev/null
@@ -1,271 +0,0 @@
-// Copyright 2022 The Flutter 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:async';
-import 'dart:convert';
-import 'package:auto_submit/service/log.dart';
-import 'package:github/github.dart';
-
-/// If a pull request was behind the tip of tree by _kBehindToT commits
-/// then the bot tries to rebase it
-const int _kBehindToT = 10;
-
-/// [GithubService] handles communication with the GitHub API.
-class GithubService {
- GithubService(this.github);
-
- final GitHub github;
-
- /// Retrieves check runs with the ref.
- Future<List<CheckRun>> getCheckRuns(
- RepositorySlug slug,
- String ref,
- ) async {
- return github.checks.checkRuns.listCheckRunsForRef(slug, ref: ref).toList();
- }
-
- Future<List<CheckRun>> getCheckRunsFiltered({
- required RepositorySlug slug,
- required String ref,
- String? checkName,
- CheckRunStatus? status,
- CheckRunFilter? filter,
- }) async {
- return github.checks.checkRuns
- .listCheckRunsForRef(
- slug,
- ref: ref,
- checkName: checkName,
- status: status,
- filter: filter,
- )
- .toList();
- }
-
- /// Fetches the specified commit.
- Future<RepositoryCommit> getCommit(RepositorySlug slug, String sha) async {
- return github.repositories.getCommit(slug, sha);
- }
-
- Future<List<PullRequestFile>> getPullRequestFiles(RepositorySlug slug, PullRequest pullRequest) async {
- final int? pullRequestId = pullRequest.number;
- final List<PullRequestFile> listPullRequestFiles = [];
-
- if (pullRequestId == null) {
- return listPullRequestFiles;
- }
-
- final Stream<PullRequestFile> pullRequestFiles = github.pullRequests.listFiles(slug, pullRequestId);
-
- await for (PullRequestFile file in pullRequestFiles) {
- listPullRequestFiles.add(file);
- }
-
- return listPullRequestFiles;
- }
-
- /// Create a new issue in github.
- Future<Issue> createIssue({
- required RepositorySlug slug,
- required String title,
- required String body,
- List<String>? labels,
- String? assignee,
- List<String>? assignees,
- String? state,
- }) async {
- final IssueRequest issueRequest = IssueRequest(
- title: title,
- body: body,
- labels: labels,
- assignee: assignee,
- assignees: assignees,
- state: state,
- );
- return github.issues.create(slug, issueRequest);
- }
-
- Future<Issue> getIssue({
- required RepositorySlug slug,
- required int issueNumber,
- }) async {
- return github.issues.get(slug, issueNumber);
- }
-
- /// Create a pull request.
- Future<PullRequest> createPullRequest({
- required RepositorySlug slug,
- String? title,
- String? head,
- required String base,
- bool draft = false,
- String? body,
- }) async {
- final CreatePullRequest createPullRequest = CreatePullRequest(title, head, base, draft: draft, body: body);
- return github.pullRequests.create(slug, createPullRequest);
- }
-
- /// Fetches the specified pull request.
- Future<PullRequest> getPullRequest(RepositorySlug slug, int pullRequestNumber) async {
- return github.pullRequests.get(slug, pullRequestNumber);
- }
-
- Future<List<PullRequest>> listPullRequests(
- RepositorySlug slug, {
- int? pages,
- String? base,
- String direction = 'desc',
- String? head,
- String sort = 'created',
- String state = 'open',
- }) async {
- final List<PullRequest> pullRequestsFound = [];
- final Stream<PullRequest> pullRequestStream = github.pullRequests.list(
- slug,
- pages: pages,
- direction: direction,
- head: head,
- sort: sort,
- state: state,
- );
- await for (PullRequest pullRequest in pullRequestStream) {
- pullRequestsFound.add(pullRequest);
- }
- return pullRequestsFound;
- }
-
- Future<bool> addReviewersToPullRequest(
- RepositorySlug slug,
- int pullRequestNumber,
- List<String> reviewerLogins,
- ) async {
- final response = await github.request(
- 'POST',
- '/repos/${slug.fullName}/pulls/$pullRequestNumber/requested_reviewers',
- body: GitHubJson.encode({'reviewers': reviewerLogins}),
- );
- return response.statusCode == StatusCodes.CREATED;
- }
-
- /// Compares two commits to fetch diff.
- ///
- /// The response will include details on the files that were changed between the two commits.
- /// Relevant APIs: https://docs.github.com/en/rest/reference/commits#compare-two-commits
- Future<GitHubComparison> compareTwoCommits(RepositorySlug slug, String refBase, String refHead) async {
- return github.repositories.compareCommits(slug, refBase, refHead);
- }
-
- /// Removes a label from a pull request.
- Future<bool> removeLabel(RepositorySlug slug, int issueNumber, String label) async {
- return github.issues.removeLabelForIssue(slug, issueNumber, label);
- }
-
- /// Add labels to a pull request.
- Future<List<IssueLabel>> addLabels(RepositorySlug slug, int issueNumber, List<String> labels) async {
- return github.issues.addLabelsToIssue(slug, issueNumber, labels);
- }
-
- /// Relevant API: https://docs.github.com/en/rest/issues/assignees?apiVersion=2022-11-28#add-assignees-to-an-issue
- Future<bool> addAssignee(RepositorySlug slug, int number, List<String> assignees) async {
- final response = await github.request(
- 'POST',
- '/repos/${slug.fullName}/issues/$number/assignees',
- body: GitHubJson.encode({'assignees': assignees}),
- );
- return response.statusCode == StatusCodes.CREATED;
- }
-
- /// Create a comment for a pull request.
- Future<IssueComment> createComment(
- RepositorySlug slug,
- int issueNumber,
- String body,
- ) async {
- return github.issues.createComment(slug, issueNumber, body);
- }
-
- /// Update a pull request branch
- Future<bool> updateBranch(RepositorySlug slug, int number, String headSha) async {
- final response = await github.request(
- 'PUT',
- '/repos/${slug.fullName}/pulls/$number/update-branch',
- body: GitHubJson.encode({'expected_head_sha': headSha}),
- );
- return response.statusCode == StatusCodes.ACCEPTED;
- }
-
- Future<Branch> getBranch(RepositorySlug slug, String branchName) async {
- return github.repositories.getBranch(slug, branchName);
- }
-
- Future<bool> deleteBranch(RepositorySlug slug, String branchName) async {
- final String ref = 'heads/$branchName';
- return github.git.deleteReference(slug, ref);
- }
-
- /// Merges a pull request according to the MergeMethod type. Current supported
- /// merge method types are merge, rebase and squash.
- Future<PullRequestMerge> mergePullRequest(
- RepositorySlug slug,
- int number, {
- String? commitMessage,
- MergeMethod mergeMethod = MergeMethod.merge,
- String? requestSha,
- }) async {
- return github.pullRequests.merge(
- slug,
- number,
- message: commitMessage,
- mergeMethod: mergeMethod,
- requestSha: requestSha,
- );
- }
-
- /// Automerges a given pull request with HEAD to ensure the commit is not in conflicting state.
- Future<void> autoMergeBranch(PullRequest pullRequest) async {
- final RepositorySlug slug = pullRequest.base!.repo!.slug();
- final int prNumber = pullRequest.number!;
- final RepositoryCommit totCommit = await getCommit(slug, 'HEAD');
- final GitHubComparison comparison = await compareTwoCommits(slug, totCommit.sha!, pullRequest.base!.sha!);
- if (comparison.behindBy! >= _kBehindToT) {
- log.info('The current branch is behind by ${comparison.behindBy} commits.');
- final String headSha = pullRequest.head!.sha!;
- await updateBranch(slug, prNumber, headSha);
- }
- }
-
- /// Get contents from a repository at the supplied path.
- Future<String> getFileContents(RepositorySlug slug, String path, {String? ref}) async {
- final RepositoryContents repositoryContents = await github.repositories.getContents(slug, path, ref: ref);
- if (!repositoryContents.isFile) {
- throw 'Contents do not point to a file.';
- }
- final String content = utf8.decode(base64.decode(repositoryContents.file!.content!.replaceAll('\n', '')));
- return content;
- }
-
- /// Check to see if user is a member of team in org.
- ///
- /// Note that we catch here as the api returns a 404 if the user has no
- /// membership in general or is not a member of the team.
- Future<bool> isTeamMember(String team, String user, String org) async {
- try {
- final TeamMembershipState teamMembershipState =
- await github.organizations.getTeamMembershipByName(org, team, user);
- return teamMembershipState.isActive;
- } on GitHubError {
- return false;
- }
- }
-
- /// Get the definition of a single repository
- Future<Repository> getRepository(RepositorySlug slug) async {
- return github.repositories.getRepository(slug);
- }
-
- Future<String> getDefaultBranch(RepositorySlug slug) async {
- final Repository repository = await getRepository(slug);
- return repository.defaultBranch;
- }
-}
diff --git a/auto_submit/lib/service/graphql_service.dart b/auto_submit/lib/service/graphql_service.dart
deleted file mode 100644
index d51fb54..0000000
--- a/auto_submit/lib/service/graphql_service.dart
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2022 The Flutter 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 'package:auto_submit/service/log.dart';
-import 'package:gql/ast.dart';
-import 'package:graphql/client.dart';
-
-import '../requests/exceptions.dart';
-
-/// Service class used to execute GraphQL queries.
-class GraphQlService {
- /// Runs a GraphQL query using [slug], [prNumber] and a [GraphQL] client.
- Future<Map<String, dynamic>> queryGraphQL({
- required DocumentNode documentNode,
- required Map<String, dynamic> variables,
- required GraphQLClient client,
- }) async {
- final QueryResult queryResult = await client.query(
- QueryOptions(
- document: documentNode,
- fetchPolicy: FetchPolicy.noCache,
- variables: variables,
- ),
- );
-
- if (queryResult.hasException) {
- log.severe(queryResult.exception.toString());
- throw const BadRequestException('GraphQL query failed');
- }
- return queryResult.data!;
- }
-
- Future<Map<String, dynamic>> mutateGraphQL({
- required DocumentNode documentNode,
- required Map<String, dynamic> variables,
- required GraphQLClient client,
- }) async {
- final QueryResult queryResult = await client.mutate(
- MutationOptions(
- document: documentNode,
- fetchPolicy: FetchPolicy.noCache,
- variables: variables,
- ),
- );
-
- if (queryResult.hasException) {
- log.severe(queryResult.exception.toString());
- throw const BadRequestException('GraphQL mutate failed');
- }
- return queryResult.data!;
- }
-}
diff --git a/auto_submit/lib/service/log.dart b/auto_submit/lib/service/log.dart
deleted file mode 100644
index 2b07c9d..0000000
--- a/auto_submit/lib/service/log.dart
+++ /dev/null
@@ -1,7 +0,0 @@
-// Copyright 2022 The Flutter 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 'package:logging/logging.dart';
-
-final Logger log = Logger('auto_submit');
diff --git a/auto_submit/lib/service/process_method.dart b/auto_submit/lib/service/process_method.dart
deleted file mode 100644
index 3a7eea6..0000000
--- a/auto_submit/lib/service/process_method.dart
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2022 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/// Enum to tell the auto-submit bot which action to take based on the label
-/// found.
-enum ProcessMethod {
- processAutosubmit,
- processRevert,
- doNotProcess,
-}
diff --git a/auto_submit/lib/service/pull_request_validation_service.dart b/auto_submit/lib/service/pull_request_validation_service.dart
deleted file mode 100644
index 3dcd8dd..0000000
--- a/auto_submit/lib/service/pull_request_validation_service.dart
+++ /dev/null
@@ -1,145 +0,0 @@
-// Copyright 2023 The Flutter 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 'package:auto_submit/configuration/repository_configuration.dart';
-import 'package:auto_submit/model/auto_submit_query_result.dart';
-import 'package:auto_submit/model/pull_request_data_types.dart';
-import 'package:auto_submit/request_handling/pubsub.dart';
-import 'package:auto_submit/service/approver_service.dart';
-import 'package:auto_submit/service/validation_service.dart';
-import 'package:auto_submit/service/config.dart';
-import 'package:auto_submit/service/github_service.dart';
-import 'package:auto_submit/service/log.dart';
-import 'package:auto_submit/service/process_method.dart';
-import 'package:auto_submit/validations/validation.dart';
-import 'package:auto_submit/validations/validation_filter.dart';
-import 'package:github/github.dart' as github;
-import 'package:retry/retry.dart';
-
-class PullRequestValidationService extends ValidationService {
- PullRequestValidationService(Config config, {RetryOptions? retryOptions})
- : super(config, retryOptions: retryOptions) {
- /// Validates a PR marked with the reverts label.
- approverService = ApproverService(config);
- }
-
- ApproverService? approverService;
-
- /// Processes a pub/sub message associated with PullRequest event.
- Future<void> processMessage(github.PullRequest messagePullRequest, String ackId, PubSub pubsub) async {
- if (await shouldProcess(messagePullRequest)) {
- await processPullRequest(
- config: config,
- result: await getNewestPullRequestInfo(config, messagePullRequest),
- messagePullRequest: messagePullRequest,
- ackId: ackId,
- pubsub: pubsub,
- );
- } else {
- log.info('Should not process ${messagePullRequest.toJson()}, and ack the message.');
- await pubsub.acknowledge('auto-submit-queue-sub', ackId);
- }
- }
-
- Future<bool> shouldProcess(github.PullRequest pullRequest) async {
- final (currentPullRequest, labelNames) = await getPrWithLabels(pullRequest);
- return (currentPullRequest.state == 'open' && labelNames.contains(Config.kAutosubmitLabel));
- }
-
- /// Processes a PullRequest running several validations to decide whether to
- /// land the commit or remove the autosubmit label.
- Future<void> processPullRequest({
- required Config config,
- required QueryResult result,
- required github.PullRequest messagePullRequest,
- required String ackId,
- required PubSub pubsub,
- }) async {
- final github.RepositorySlug slug = messagePullRequest.base!.repo!.slug();
- final int prNumber = messagePullRequest.number!;
- final RepositoryConfiguration repositoryConfiguration = await config.getRepositoryConfiguration(slug);
-
- // filter out validations here
- final ValidationFilter validationFilter = ValidationFilter(
- config: config,
- processMethod: ProcessMethod.processAutosubmit,
- repositoryConfiguration: repositoryConfiguration,
- );
- final Set<Validation> validations = validationFilter.getValidations();
-
- final Map<String, ValidationResult> validationsMap = <String, ValidationResult>{};
- final GithubService githubService = await config.createGithubService(slug);
-
- // get the labels before validation so that we can detect all labels.
- // TODO (https://github.com/flutter/flutter/issues/132811) remove this after graphql is removed.
- final github.PullRequest updatedPullRequest = await githubService.getPullRequest(slug, messagePullRequest.number!);
-
- /// Runs all the validation defined in the service.
- /// If the runCi flag is false then we need a way to not run the ciSuccessful validation.
- for (Validation validation in validations) {
- log.info('${slug.fullName}/$prNumber running validation ${validation.name}');
- final ValidationResult validationResult = await validation.validate(
- result,
- updatedPullRequest,
- );
- validationsMap[validation.name] = validationResult;
- }
-
- /// If there is at least one action that requires to remove label do so and add comments for all the failures.
- bool shouldReturn = false;
- for (MapEntry<String, ValidationResult> result in validationsMap.entries) {
- if (!result.value.result && result.value.action == Action.REMOVE_LABEL) {
- final String commmentMessage = result.value.message.isEmpty ? 'Validations Fail.' : result.value.message;
- final String message = 'auto label is removed for ${slug.fullName}/$prNumber, due to $commmentMessage';
- await githubService.removeLabel(slug, prNumber, Config.kAutosubmitLabel);
- await githubService.createComment(slug, prNumber, message);
- log.info(message);
- shouldReturn = true;
- }
- }
-
- if (shouldReturn) {
- log.info(
- 'The pr ${slug.fullName}/$prNumber with message: $ackId should be acknowledged due to validation failure.',
- );
- await pubsub.acknowledge('auto-submit-queue-sub', ackId);
- log.info('The pr ${slug.fullName}/$prNumber is not feasible for merge and message: $ackId is acknowledged.');
- return;
- }
-
- // If PR has some failures to ignore temporarily do nothing and continue.
- for (MapEntry<String, ValidationResult> result in validationsMap.entries) {
- if (!result.value.result && result.value.action == Action.IGNORE_TEMPORARILY) {
- log.info(
- 'Temporarily ignoring processing of ${slug.fullName}/$prNumber due to ${result.key} failing validation.',
- );
- return;
- }
- }
-
- // If we got to this point it means we are ready to submit the PR.
- final MergeResult processed = await processMerge(
- config: config,
- messagePullRequest: messagePullRequest,
- );
-
- if (!processed.result) {
- final String message = 'auto label is removed for ${slug.fullName}/$prNumber, ${processed.message}.';
- await githubService.removeLabel(slug, prNumber, Config.kAutosubmitLabel);
- await githubService.createComment(slug, prNumber, message);
- log.info(message);
- } else {
- log.info('Pull Request ${slug.fullName}/$prNumber was merged successfully!');
- log.info('Attempting to insert a pull request record into the database for $prNumber');
- await insertPullRequestRecord(
- config: config,
- pullRequest: messagePullRequest,
- pullRequestType: PullRequestChangeType.change,
- );
- }
-
- log.info('Ack the processed message : $ackId.');
- await pubsub.acknowledge('auto-submit-queue-sub', ackId);
- }
-}
diff --git a/auto_submit/lib/service/revert_issue_body_formatter.dart b/auto_submit/lib/service/revert_issue_body_formatter.dart
deleted file mode 100644
index c41e5a4..0000000
--- a/auto_submit/lib/service/revert_issue_body_formatter.dart
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2023 The Flutter 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 'package:github/github.dart';
-
-class RevertIssueBodyFormatter {
- RevertIssueBodyFormatter({
- required this.slug,
- required this.originalPrNumber,
- required this.initiatingAuthor,
- required this.originalPrTitle,
- required this.originalPrBody,
- });
-
- // Possible format for the revert issue
- RepositorySlug slug;
- String initiatingAuthor;
- int originalPrNumber;
- // These two fields can be null in the original pull request.
- String? originalPrTitle;
- String? originalPrBody;
-
- late String? revertPrTitle;
- late String? revertPrBody;
- late String? revertPrLink;
-
- RevertIssueBodyFormatter get format {
- // Create the title for the revert issue.
- originalPrTitle ??= 'No title provided.';
- revertPrTitle = 'Reverts "$originalPrTitle"';
-
- // create the reverts Link for the body. Looks like Reverts flutter/cocoon#123 but will render as a link.
- revertPrLink = 'Reverts ${slug.fullName}#$originalPrNumber';
-
- originalPrBody ??= 'No description provided.';
-
- // Create the body for the revert issue.
- revertPrBody = '''
-$revertPrLink
-Initiated by: $initiatingAuthor
-This change reverts the following previous change:
-Original Description:
-$originalPrBody
-''';
-
- return this;
- }
-
- String? get formattedRevertPrTitle => revertPrTitle;
-
- String? get formattedRevertPrBody => revertPrBody;
-}
diff --git a/auto_submit/lib/service/revert_request_validation_service.dart b/auto_submit/lib/service/revert_request_validation_service.dart
deleted file mode 100644
index e1fe761..0000000
--- a/auto_submit/lib/service/revert_request_validation_service.dart
+++ /dev/null
@@ -1,263 +0,0 @@
-// Copyright 2023 The Flutter 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 'package:auto_submit/action/git_cli_revert_method.dart';
-import 'package:auto_submit/configuration/repository_configuration.dart';
-import 'package:auto_submit/model/auto_submit_query_result.dart';
-import 'package:auto_submit/model/pull_request_data_types.dart';
-import 'package:auto_submit/request_handling/pubsub.dart';
-import 'package:auto_submit/requests/github_pull_request_event.dart';
-import 'package:auto_submit/service/approver_service.dart';
-import 'package:auto_submit/service/validation_service.dart';
-import 'package:auto_submit/service/config.dart';
-import 'package:auto_submit/service/github_service.dart';
-import 'package:auto_submit/service/log.dart';
-import 'package:auto_submit/validations/validation.dart';
-import 'package:auto_submit/validations/validation_filter.dart';
-import 'package:github/github.dart' as github;
-import 'package:meta/meta.dart';
-import 'package:retry/retry.dart';
-import 'package:auto_submit/action/revert_method.dart';
-import 'process_method.dart';
-
-enum RevertProcessMethod { revert, revertOf, none }
-
-class RevertRequestValidationService extends ValidationService {
- RevertRequestValidationService(Config config, {RetryOptions? retryOptions, RevertMethod? revertMethod})
- : revertMethod = revertMethod ?? GitCliRevertMethod(),
- super(config, retryOptions: retryOptions) {
- /// Validates a PR marked with the reverts label.
- approverService = ApproverService(config);
- }
-
- ApproverService? approverService;
- @visibleForTesting
- RevertMethod? revertMethod;
- @visibleForTesting
- ValidationFilter? validationFilter;
-
- /// TODO run the actual request from here and remove the shouldProcess call.
- /// Processes a pub/sub message associated with PullRequest event.
- Future<void> processMessage(GithubPullRequestEvent githubPullRequestEvent, String ackId, PubSub pubsub) async {
- // Make sure the pull request still contains the labels.
- final github.PullRequest messagePullRequest = githubPullRequestEvent.pullRequest!;
- final (currentPullRequest, labelNames) = await getPrWithLabels(messagePullRequest);
- final RevertProcessMethod revertProcessMethod = await shouldProcess(currentPullRequest, labelNames);
-
- final GithubPullRequestEvent updatedGithubPullRequestEvent = GithubPullRequestEvent(
- pullRequest: currentPullRequest,
- action: githubPullRequestEvent.action,
- sender: githubPullRequestEvent.sender,
- );
-
- switch (revertProcessMethod) {
- // Revert is the processing of the closed issue.
- case RevertProcessMethod.revert:
- await processRevertRequest(
- result: await getNewestPullRequestInfo(config, messagePullRequest),
- githubPullRequestEvent: updatedGithubPullRequestEvent,
- ackId: ackId,
- pubsub: pubsub,
- );
- break;
- // Reverts is the processing of the opened revert issue.
- case RevertProcessMethod.revertOf:
- await processRevertOfRequest(
- result: await getNewestPullRequestInfo(config, messagePullRequest),
- githubPullRequestEvent: updatedGithubPullRequestEvent,
- ackId: ackId,
- pubsub: pubsub,
- );
- break;
- // Do not process.
- case RevertProcessMethod.none:
- log.info('Should not process ${messagePullRequest.toJson()}, and ack the message.');
- await pubsub.acknowledge(config.pubsubRevertRequestSubscription, ackId);
- break;
- }
- }
-
- /// Check whether the original request is within the 24 hour time limit to revert.
- bool isWithinTimeLimit(github.PullRequest pullRequest) {
- if (pullRequest.mergedAt == null) {
- // This pull request has never been merged.
- return false;
- }
- return DateTime.now().difference(pullRequest.mergedAt!).inHours <= 24;
- }
-
- /// Determine if we should process the incoming pull request webhook event.
- Future<RevertProcessMethod> shouldProcess(github.PullRequest pullRequest, List<String> labelNames) async {
- // This is the initial revert request state.
- if (pullRequest.state == 'closed' && labelNames.contains(Config.kRevertLabel) && pullRequest.mergedAt != null) {
- return RevertProcessMethod.revert;
- } else if (pullRequest.state == 'open' &&
- labelNames.contains(Config.kRevertOfLabel) &&
- pullRequest.user!.login == 'auto-submit[bot]') {
- // This is the path where we check validations
- return RevertProcessMethod.revertOf;
- }
- return RevertProcessMethod.none;
- }
-
- // pullRequest.state == 'closed' && labelNames.contains('revert')
- // TODO need a way to stop processing this.
- Future<void> processRevertRequest({
- required QueryResult result,
- required GithubPullRequestEvent githubPullRequestEvent,
- required String ackId,
- required PubSub pubsub,
- }) async {
- final github.PullRequest messagePullRequest = githubPullRequestEvent.pullRequest!;
- final github.RepositorySlug slug = messagePullRequest.base!.repo!.slug();
- final GithubService githubService = await config.createGithubService(slug);
- final String sender = githubPullRequestEvent.sender!.login!;
-
- if (!isWithinTimeLimit(messagePullRequest)) {
- final String message = '''Time to revert pull request ${slug.fullName}/${messagePullRequest.number} has elapsed.
- You need to open the revert manually and process as a regular pull request.''';
- log.info(message);
- await githubService.createComment(slug, messagePullRequest.number!, message);
- await githubService.removeLabel(slug, messagePullRequest.number!, Config.kRevertLabel);
- log.info('Should not process ${messagePullRequest.toJson()}, and ack the message.');
- await pubsub.acknowledge(config.pubsubRevertRequestSubscription, ackId);
- return;
- }
-
- // Attempt to create the new revert pull request.
- try {
- // This is the autosubmit query result pull request from graphql.
- final github.PullRequest pullRequest =
- await revertMethod!.createRevert(config, sender, messagePullRequest) as github.PullRequest;
- log.info('Created revert pull request ${slug.fullName}/${pullRequest.number}.');
- // This will come through this service again for processing.
- await githubService.addLabels(slug, pullRequest.number!, [Config.kRevertOfLabel]);
- log.info('Assigning new revert issue to $sender');
- await githubService.addAssignee(slug, pullRequest.number!, [sender]);
- // TODO (ricardoamador) create a better solution than this to stop processing
- // the revert requests. Maybe change the label after the revert has occurred.
- // For some reason we get duplicate events even though we ack the message.
- await githubService.removeLabel(slug, messagePullRequest.number!, Config.kRevertLabel);
- // Notify the discord tree channel that the revert issue has been created
- // and will be processed.
- } catch (e) {
- final String message = 'Unable to create the revert pull request due to ${e.toString()}';
- log.severe(message);
- await githubService.createComment(slug, messagePullRequest.number!, message);
- await githubService.removeLabel(slug, messagePullRequest.number!, Config.kRevertLabel);
- } finally {
- await pubsub.acknowledge(config.pubsubRevertRequestSubscription, ackId);
- }
- }
-
- /// Processes a PullRequest running several validations to decide whether to
- /// land the commit or remove the label.
- // pullRequest.state == 'open' && labelNames.contains('revert of')
- Future<void> processRevertOfRequest({
- required QueryResult result,
- required GithubPullRequestEvent githubPullRequestEvent,
- required String ackId,
- required PubSub pubsub,
- }) async {
- final github.PullRequest messagePullRequest = githubPullRequestEvent.pullRequest!;
- final github.RepositorySlug slug = messagePullRequest.base!.repo!.slug();
- final GithubService githubService = await config.createGithubService(slug);
- final int prNumber = messagePullRequest.number!;
-
- // Check to make sure the repository allows review-less revert pull requests
- // so that we can reassign if needed otherwise autoapprove the pull request.
- final RepositoryConfiguration repositoryConfiguration = await config.getRepositoryConfiguration(slug);
- if (!repositoryConfiguration.supportNoReviewReverts) {
- await githubService.removeLabel(slug, prNumber, Config.kRevertOfLabel);
- await githubService.createComment(
- slug,
- prNumber,
- 'Repository configuration does not support review-less revert pull requests. Please assign at least two reviewers to this pull request.',
- );
- // We do not want to continue processing this issue.
- log.info('Ack the processed message : $ackId.');
- await pubsub.acknowledge(config.pubsubRevertRequestSubscription, ackId);
- return;
- }
-
- validationFilter ??= ValidationFilter(
- config: config,
- processMethod: ProcessMethod.processRevert,
- repositoryConfiguration: repositoryConfiguration,
- );
-
- final Set<Validation> validations = validationFilter!.getValidations();
-
- final Map<String, ValidationResult> validationsMap = <String, ValidationResult>{};
-
- /// Runs all the validation defined in the service.
- /// If the runCi flag is false then we need a way to not run the ciSuccessful validation.
- for (Validation validation in validations) {
- log.info('${slug.fullName}/$prNumber running validation ${validation.name}');
- validationsMap[validation.name] = await validation.validate(
- result,
- // this needs to be the newly opened pull request.
- messagePullRequest,
- );
- }
-
- /// If there is at least one action that requires to remove label do so and add comments for all the failures.
- bool shouldReturn = false;
- for (MapEntry<String, ValidationResult> result in validationsMap.entries) {
- if (!result.value.result && result.value.action == Action.REMOVE_LABEL) {
- final String commmentMessage = result.value.message.isEmpty ? 'Validations Fail.' : result.value.message;
- final String message = 'auto label is removed for ${slug.fullName}/$prNumber, due to $commmentMessage';
- await githubService.removeLabel(slug, prNumber, Config.kRevertOfLabel);
- await githubService.createComment(slug, prNumber, message);
- log.info(message);
- shouldReturn = true;
- }
- }
-
- if (shouldReturn) {
- log.info(
- 'The pr ${slug.fullName}/$prNumber with message: $ackId should be acknowledged due to validation failure.',
- );
- log.info('The pr ${slug.fullName}/$prNumber is not feasible for merge and message: $ackId is acknowledged.');
- await pubsub.acknowledge(config.pubsubRevertRequestSubscription, ackId);
- return;
- }
-
- // If PR has some failures to ignore temporarily do nothing and continue.
- for (MapEntry<String, ValidationResult> result in validationsMap.entries) {
- if (!result.value.result && result.value.action == Action.IGNORE_TEMPORARILY) {
- log.info(
- 'Temporarily ignoring processing of ${slug.fullName}/$prNumber due to ${result.key} failing validation.',
- );
- return;
- }
- }
-
- // If we got to this point it means we are ready to submit the PR.
- final MergeResult processed = await processMerge(
- config: config,
- messagePullRequest: messagePullRequest,
- );
-
- if (!processed.result) {
- final String message = 'auto label is removed for ${slug.fullName}/$prNumber, ${processed.message}.';
- await githubService.removeLabel(slug, prNumber, Config.kRevertOfLabel);
- await githubService.createComment(slug, prNumber, message);
- log.info(message);
- } else {
- log.info('Revert merged successfully, deleting branch ${messagePullRequest.head!.ref!}');
- await githubService.deleteBranch(slug, messagePullRequest.head!.ref!);
- log.info('Pull Request ${slug.fullName}/$prNumber was merged successfully!');
- log.info('Attempting to insert a pull request record into the database for $prNumber');
- await insertPullRequestRecord(
- config: config,
- pullRequest: messagePullRequest,
- pullRequestType: PullRequestChangeType.revert,
- );
- }
-
- log.info('Ack the processed message : $ackId.');
- await pubsub.acknowledge(config.pubsubRevertRequestSubscription, ackId);
- }
-}
diff --git a/auto_submit/lib/service/secrets.dart b/auto_submit/lib/service/secrets.dart
deleted file mode 100644
index a1e33e7..0000000
--- a/auto_submit/lib/service/secrets.dart
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2022 The Flutter 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:convert';
-import 'dart:io';
-
-import 'package:auto_submit/service/config.dart';
-import 'package:appengine/appengine.dart';
-import 'package:googleapis/secretmanager/v1.dart';
-
-/// Access secrets for Google Cloud projects.
-///
-/// See also:
-/// * https://cloud.google.com/secret-manager/docs
-abstract class SecretManager {
- const SecretManager();
-
- Future<String> get(String key);
-
- void put(String key, [String? value]);
-}
-
-class CloudSecretManager implements SecretManager {
- CloudSecretManager();
-
- final String projectId = Platform.environment['APPLICATION_ID'] ?? Config.flutterGcpProjectId;
-
- @override
- Future<String> get(
- String key, {
- String? fields,
- }) async {
- final SecretManagerApi api = SecretManagerApi(authClientService);
- final SecretPayload? payload = (await api.projects.secrets.versions.access(
- 'projects/$projectId/secrets/$key/versions/latest',
- $fields: fields,
- ))
- .payload;
- if (payload?.data == null) {
- throw SecretManagerException('Failed to find secret for $key with \$fields=$fields');
- }
- return String.fromCharCodes(base64Decode(payload!.data!));
- }
-
- @override
- void put(String key, [String? value]) => throw UnimplementedError('put is only supported for local runs');
-}
-
-/// Local instance of [SecretManager] for use in testing.
-class LocalSecretManager implements SecretManager {
- final Map<String, String?> _secrets = <String, String>{};
-
- @override
- Future<String> get(String key, {String? fields}) async {
- if (_secrets.containsKey(key)) {
- return _secrets[key]!;
- } else if (Platform.environment.containsKey(key)) {
- return Platform.environment[key]!;
- }
-
- throw Exception('Failed to find $key in environment. Try adding it to the environment variables');
- }
-
- @override
- void put(String key, [String? value]) {
- _secrets[key] = value;
- }
-}
-
-class SecretManagerException implements Exception {
- SecretManagerException([this.message]);
-
- final String? message;
-
- @override
- String toString() => 'SecretManagerException: $message';
-}
diff --git a/auto_submit/lib/service/validation_service.dart b/auto_submit/lib/service/validation_service.dart
deleted file mode 100644
index 0567002..0000000
--- a/auto_submit/lib/service/validation_service.dart
+++ /dev/null
@@ -1,223 +0,0 @@
-// Copyright 2023 The Flutter 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 'package:auto_submit/exception/bigquery_exception.dart';
-import 'package:auto_submit/exception/retryable_exception.dart';
-import 'package:auto_submit/model/auto_submit_query_result.dart';
-import 'package:auto_submit/model/big_query_pull_request_record.dart';
-import 'package:auto_submit/model/pull_request_data_types.dart';
-import 'package:auto_submit/requests/graphql_queries.dart';
-import 'package:auto_submit/service/bigquery.dart';
-import 'package:auto_submit/service/config.dart';
-import 'package:auto_submit/service/github_service.dart';
-import 'package:auto_submit/service/graphql_service.dart';
-import 'package:auto_submit/service/log.dart';
-import 'package:github/github.dart' as github;
-import 'package:graphql/client.dart' as graphql;
-import 'package:retry/retry.dart';
-
-/// Class containing common methods to each of the pull request type validation
-/// services.
-class ValidationService {
- ValidationService(this.config, {RetryOptions? retryOptions})
- : retryOptions = retryOptions ?? Config.mergeRetryOptions;
-
- final Config config;
- final RetryOptions retryOptions;
-
- /// Fetch the most up to date info for the current pull request from github.
- Future<QueryResult> getNewestPullRequestInfo(Config config, github.PullRequest pullRequest) async {
- final github.RepositorySlug slug = pullRequest.base!.repo!.slug();
- final graphql.GraphQLClient graphQLClient = await config.createGitHubGraphQLClient(slug);
- final int? prNumber = pullRequest.number;
- final GraphQlService graphQlService = GraphQlService();
-
- final FindPullRequestsWithReviewsQuery findPullRequestsWithReviewsQuery = FindPullRequestsWithReviewsQuery(
- repositoryOwner: slug.owner,
- repositoryName: slug.name,
- pullRequestNumber: prNumber!,
- );
-
- final Map<String, dynamic> data = await graphQlService.queryGraphQL(
- documentNode: findPullRequestsWithReviewsQuery.documentNode,
- variables: findPullRequestsWithReviewsQuery.variables,
- client: graphQLClient,
- );
-
- return QueryResult.fromJson(data);
- }
-
- Future<(github.PullRequest, List<String>)> getPrWithLabels(github.PullRequest pullRequest) async {
- final github.RepositorySlug slug = pullRequest.base!.repo!.slug();
- final GithubService githubService = await config.createGithubService(slug);
- final github.PullRequest currentPullRequest = await githubService.getPullRequest(slug, pullRequest.number!);
- final List<String> labelNames = (currentPullRequest.labels as List<github.IssueLabel>)
- .map<String>((github.IssueLabel labelMap) => labelMap.name)
- .toList();
- return (currentPullRequest, labelNames);
- }
-
- /// Merges the commit if the PullRequest passes all the validations.
- Future<MergeResult> processMerge({
- required Config config,
- required github.PullRequest messagePullRequest,
- }) async {
- final github.RepositorySlug slug = messagePullRequest.base!.repo!.slug();
- final int number = messagePullRequest.number!;
-
- // Pass an explicit commit message from the PR title otherwise the GitHub API will use the first commit message.
- const String revertPattern = 'Revert "Revert';
- String messagePrefix = '';
-
- if (messagePullRequest.title!.contains(revertPattern)) {
- // Cleanup auto-generated revert messages.
- messagePrefix = '''
-${messagePullRequest.title!.replaceFirst('Revert "Revert', 'Reland')}
-
-''';
- }
-
- final String prBody = _sanitizePrBody(messagePullRequest.body ?? '');
- final String commitMessage = '$messagePrefix$prBody';
-
- try {
- github.PullRequestMerge? result;
-
- await retryOptions.retry(
- () async {
- result = await _processMergeInternal(
- config: config,
- commitMessage: commitMessage,
- slug: slug,
- number: number,
- // TODO(ricardoamador): make this configurable per repository, https://github.com/flutter/flutter/issues/114557
- mergeMethod: github.MergeMethod.squash,
- );
- },
- retryIf: (Exception e) => e is RetryableException,
- );
-
- final bool merged = result?.merged ?? false;
- if (result != null && !merged) {
- final String message = 'Failed to merge ${slug.fullName}/$number with ${result?.message}';
- log.severe(message);
- return (result: false, message: message);
- }
- } catch (e) {
- // Catch graphql client init exceptions.
- final String message = 'Failed to merge ${slug.fullName}/$number with ${e.toString()}';
- log.severe(message);
- return (result: false, message: message);
- }
-
- return (result: true, message: commitMessage);
- }
-
- /// Insert a merged pull request record into the database.
- Future<void> insertPullRequestRecord({
- required Config config,
- required github.PullRequest pullRequest,
- required PullRequestChangeType pullRequestType,
- }) async {
- final github.RepositorySlug slug = pullRequest.base!.repo!.slug();
- final GithubService gitHubService = await config.createGithubService(slug);
- // We need the updated time fields for the merged request from github.
- final github.PullRequest currentPullRequest = await gitHubService.getPullRequest(slug, pullRequest.number!);
-
- log.info('Updated pull request info for ${slug.fullName}/${pullRequest.number}');
-
- // add a record for the pull request into our metrics tracking
- final PullRequestRecord pullRequestRecord = PullRequestRecord(
- organization: currentPullRequest.base!.repo!.slug().owner,
- repository: currentPullRequest.base!.repo!.slug().name,
- author: currentPullRequest.user!.login,
- prNumber: pullRequest.number!,
- prCommit: currentPullRequest.head!.sha,
- prRequestType: pullRequestType.name,
- prCreatedTimestamp: currentPullRequest.createdAt!,
- prLandedTimestamp: currentPullRequest.closedAt!,
- );
-
- log.info('Created pull request record: ${pullRequestRecord.toString()}');
-
- try {
- final BigqueryService bigqueryService = await config.createBigQueryService();
- await bigqueryService.insertPullRequestRecord(
- projectId: Config.flutterGcpProjectId,
- pullRequestRecord: pullRequestRecord,
- );
- log.info('Record inserted for pull request ${slug.fullName}/${pullRequest.number} successfully.');
- } on BigQueryException catch (exception) {
- log.severe(
- 'Unable to insert pull request record for pull request ${slug.fullName}/${pullRequest.number} due to: ${exception.toString()}',
- );
- }
- }
-}
-
-/// Small wrapper class to allow us to capture and create a comment in the PR with
-/// the issue that caused the merge failure.
-typedef MergeResult = ({bool result, String message});
-
-/// Function signature that will be executed with retries.
-typedef RetryHandler = Function();
-
-/// Internal wrapper for the logic of merging a pull request into github.
-Future<github.PullRequestMerge> _processMergeInternal({
- required Config config,
- required github.RepositorySlug slug,
- required int number,
- required github.MergeMethod mergeMethod,
- String? commitMessage,
- String? requestSha,
-}) async {
- // This is retryable so to guard against token expiration we get a fresh
- // client each time.
- log.info('Attempting to merge ${slug.fullName}/$number.');
- final GithubService gitHubService = await config.createGithubService(slug);
- final github.PullRequestMerge pullRequestMerge = await gitHubService.mergePullRequest(
- slug,
- number,
- commitMessage: commitMessage,
- mergeMethod: mergeMethod,
- requestSha: requestSha,
- );
-
- if (pullRequestMerge.merged != true) {
- throw RetryableException('Pull request ${slug.fullName}/$number could not be merged: ${pullRequestMerge.message}');
- }
-
- return pullRequestMerge;
-}
-
-final RegExp _kCheckboxPattern = RegExp(r'^\s*-[ ]?\[( |x|X)\]');
-final RegExp _kCommentPattern = RegExp(r'<!--.*-->');
-final RegExp _kMarkdownLinkRefDef = RegExp(r'^\[[\w\/ -]+\]:');
-final RegExp _kPreLaunchHeader = RegExp(r'## Pre-launch Checklist');
-final RegExp _kDiscordPattern = RegExp(r'#hackers-new');
-
-String _sanitizePrBody(String rawPrBody) {
- final buffer = StringBuffer();
- bool lastLineWasEmpty = false;
- for (final line in rawPrBody.split('\n')) {
- if (_kCheckboxPattern.hasMatch(line) ||
- _kCommentPattern.hasMatch(line) ||
- _kMarkdownLinkRefDef.hasMatch(line) ||
- _kPreLaunchHeader.hasMatch(line) ||
- _kDiscordPattern.hasMatch(line)) {
- continue;
- }
- if (line.trim().isEmpty) {
- // we don't need to include multiple empty lines
- if (lastLineWasEmpty) {
- continue;
- }
- lastLineWasEmpty = true;
- } else {
- lastLineWasEmpty = false;
- }
- buffer.writeln(line);
- }
- return buffer.toString().trim();
-}
diff --git a/auto_submit/lib/validations/approval.dart b/auto_submit/lib/validations/approval.dart
deleted file mode 100644
index ee87b0f..0000000
--- a/auto_submit/lib/validations/approval.dart
+++ /dev/null
@@ -1,180 +0,0 @@
-// Copyright 2022 The Flutter 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 'package:auto_submit/configuration/repository_configuration.dart';
-import 'package:auto_submit/service/config.dart';
-import 'package:auto_submit/model/auto_submit_query_result.dart';
-import 'package:auto_submit/service/github_service.dart';
-import 'package:auto_submit/validations/validation.dart';
-import 'package:github/github.dart' as github;
-
-import '../service/log.dart';
-
-/// Validates that a PR has been approved in accordance with the flutter code
-/// review guidelines.
-class Approval extends Validation {
- Approval({
- required super.config,
- });
-
- @override
- String get name => 'Approval';
-
- /// Implements the code review approval logic.
- @override
- Future<ValidationResult> validate(QueryResult result, github.PullRequest messagePullRequest) async {
- final PullRequest pullRequest = result.repository!.pullRequest!;
- final String? author = pullRequest.author!.login;
- final List<ReviewNode> reviews = pullRequest.reviews!.nodes!;
- final github.RepositorySlug slug = github.RepositorySlug.full(messagePullRequest.base!.repo!.fullName);
-
- final RepositoryConfiguration repositoryConfiguration = await config.getRepositoryConfiguration(slug);
-
- bool approved = false;
- String message = '';
- Action action = Action.REMOVE_LABEL;
- if (repositoryConfiguration.autoApprovalAccounts.contains(author)) {
- log.info('PR ${slug.fullName}/${messagePullRequest.number} approved for roller account: $author');
- return ValidationResult(true, Action.REMOVE_LABEL, '');
- } else {
- final GithubService githubService = await config.createGithubService(slug);
- final bool authorIsFlutterHacker =
- await githubService.isTeamMember(repositoryConfiguration.approvalGroup, author!, slug.owner);
- final Approver approver = Approver(
- slug,
- repositoryConfiguration,
- githubService,
- author,
- reviews,
- );
- await approver.computeApproval();
- approved = approver.approved;
-
- log.info(
- 'PR ${slug.fullName}/${messagePullRequest.number} approved $approved, approvers: ${approver.approvers}, remaining approvals: ${approver.remainingReviews}, request authors: ${approver.changeRequestAuthors}',
- );
-
- String approvedMessage;
- final String flutterHackerMessage = (authorIsFlutterHacker)
- ? 'You are a member of ${repositoryConfiguration.approvalGroup}'
- : 'You are not a member of ${repositoryConfiguration.approvalGroup}';
- // Changes were requested, review count does not matter.
- if (approver.changeRequestAuthors.isNotEmpty) {
- approved = false;
- approvedMessage =
- 'This PR has not met approval requirements for merging. Changes were requested by ${approver.changeRequestAuthors}, please make the needed changes and resubmit this PR.\n'
- '$flutterHackerMessage and need ${approver.remainingReviews} more review(s) in order to merge this PR.\n';
- } else {
- // No changes were requested so check approval count.
- approvedMessage = approved
- ? 'This PR has met approval requirements for merging.\n'
- : 'This PR has not met approval requirements for merging. $flutterHackerMessage and need ${approver.remainingReviews} more review(s) in order to merge this PR.\n';
- if (!approved && authorIsFlutterHacker) {
- // Flutter hackers are aware of the review requirements, and can add
- // the autosubmit label without waiting on review.
- action = Action.IGNORE_TEMPORARILY;
- }
- }
-
- message = approved ? approvedMessage : '$approvedMessage\n${Config.pullRequestApprovalRequirementsMessage}';
- }
-
- return ValidationResult(approved, action, message);
- }
-}
-
-class Approver {
- Approver(
- this.slug,
- this.repositoryConfiguration,
- this.githubService,
- this.author,
- this.reviews,
- );
-
- final github.RepositorySlug slug;
- final RepositoryConfiguration repositoryConfiguration;
- final GithubService githubService;
- final String? author;
- final List<ReviewNode> reviews;
-
- bool _approved = false;
- int _remainingReviews = 2;
- final Set<String?> _approvers = <String?>{};
- final Set<String?> _changeRequestAuthors = <String?>{};
- final Set<String?> _reviewAuthors = <String?>{};
-
- bool get approved => _approved;
-
- int get remainingReviews => _remainingReviews;
-
- Set<String?> get approvers => _approvers;
-
- Set<String?> get changeRequestAuthors => _changeRequestAuthors;
-
- /// Parses the restApi response reviews.
- ///
- /// If author is a of the defined approval group then it only requires a
- /// single review from another approval group member. If the author is not a
- /// member of the approval group then it will require two reviews from members
- /// of the approval group.
- ///
- /// Changes requested will supercede any approvals and the autosubmit bot will
- /// not make any merges until the change requests are fixed.
- Future<void> computeApproval() async {
- _remainingReviews = repositoryConfiguration.approvingReviews;
- // TODO (ricardoamador) team might be more than one in the future.
- final bool authorIsMember =
- await githubService.isTeamMember(repositoryConfiguration.approvalGroup, author!, slug.owner);
-
- // The author counts as 1 review if they are a member of the approval group
- // so we need only 1 more review from a member of the approval group.
- if (authorIsMember) {
- _remainingReviews--;
- _approvers.add(author);
- }
-
- final int targetReviewCount = _remainingReviews;
-
- // Github reviews are returned in chonological order so to avoid the odd
- // case where a user requests changes then approves we parse the reviews in
- // reverse chronological order.
- for (ReviewNode review in reviews.reversed) {
- if (review.author!.login == author) {
- log.info('Author cannot review own pull request.');
- continue;
- }
-
- // Ignore reviews from non-members/owners.
- if (!await githubService.isTeamMember(
- repositoryConfiguration.approvalGroup,
- review.author!.login!,
- slug.owner,
- )) {
- continue;
- }
-
- final String? state = review.state;
- final String? authorLogin = review.author!.login;
- // Github keeps all reviews so the same person can provide two reviews and
- // possibly bypass the two review rule. Track the reviewers so we can
- // account for this.
- if (state == APPROVED_STATE && !_reviewAuthors.contains(authorLogin)) {
- _approvers.add(authorLogin);
- if (_remainingReviews > 0) {
- _remainingReviews--;
- }
- } else if (state == CHANGES_REQUESTED_STATE && !_reviewAuthors.contains(authorLogin)) {
- _changeRequestAuthors.add(authorLogin);
- if (_remainingReviews < targetReviewCount) {
- _remainingReviews++;
- }
- }
-
- _reviewAuthors.add(authorLogin);
- }
-
- _approved = (_approvers.length > repositoryConfiguration.approvingReviews - 1) && _changeRequestAuthors.isEmpty;
- }
-}
diff --git a/auto_submit/lib/validations/ci_successful.dart b/auto_submit/lib/validations/ci_successful.dart
deleted file mode 100644
index f7a9cfb..0000000
--- a/auto_submit/lib/validations/ci_successful.dart
+++ /dev/null
@@ -1,199 +0,0 @@
-// Copyright 2022 The Flutter 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 'package:auto_submit/configuration/repository_configuration.dart';
-import 'package:auto_submit/model/auto_submit_query_result.dart';
-import 'package:auto_submit/service/github_service.dart';
-import 'package:auto_submit/validations/validation.dart';
-import 'package:github/github.dart' as github;
-
-import '../service/config.dart';
-import '../service/log.dart';
-
-/// Validates all the CI build/tests ran and were successful.
-class CiSuccessful extends Validation {
- /// The status checks that are not related to changes in this PR.
- static const Set<String> notInAuthorsControl = <String>{
- 'tree-status', // flutter/engine repo
- 'submit-queue', // packages repo
- };
-
- CiSuccessful({
- required super.config,
- });
-
- @override
- String get name => 'CiSuccessful';
-
- @override
-
- /// Implements the CI build/tests validations.
- Future<ValidationResult> validate(QueryResult result, github.PullRequest messagePullRequest) async {
- bool allSuccess = true;
- final github.RepositorySlug slug = messagePullRequest.base!.repo!.slug();
- final int prNumber = messagePullRequest.number!;
- final PullRequest pullRequest = result.repository!.pullRequest!;
- final Set<FailureDetail> failures = <FailureDetail>{};
-
- final List<ContextNode> statuses = <ContextNode>[];
- final Commit commit = pullRequest.commits!.nodes!.single.commit!;
- final Author author = result.repository!.pullRequest!.author!;
-
- // Recently most of the repositories have migrated away of using the status
- // APIs and for those repos commit.status is null.
- if (commit.status != null && commit.status!.contexts!.isNotEmpty) {
- statuses.addAll(commit.status!.contexts!);
- }
-
- final RepositoryConfiguration repositoryConfiguration = await config.getRepositoryConfiguration(slug);
- final String targetBranch = repositoryConfiguration.defaultBranch;
- // Check tree status of repos. If the tree status is not ready,
- // we want to hold and wait for the status, same as waiting
- // for checks to finish.
- final String? baseBranch = messagePullRequest.base!.ref;
- if (baseBranch == targetBranch) {
- // Only validate tree status where base branch is the default branch.
- if (!treeStatusCheck(slug, prNumber, statuses)) {
- log.warning('Statuses were not ready for ${slug.fullName}/$prNumber, sha: $commit.');
- return ValidationResult(false, Action.IGNORE_TEMPORARILY, 'Hold to wait for the tree status ready.');
- }
- } else {
- log.info('Target branch is $baseBranch for ${slug.fullName}/$prNumber, skipping tree status check.');
- }
-
- // List of labels associated with the pull request.
- final List<String> labelNames = (messagePullRequest.labels as List<github.IssueLabel>)
- .map<String>((github.IssueLabel labelMap) => labelMap.name)
- .toList();
-
- /// Validate if all statuses have been successful.
- allSuccess = validateStatuses(slug, prNumber, author, labelNames, statuses, failures, allSuccess);
-
- final GithubService gitHubService = await config.createGithubService(slug);
- final String? sha = commit.oid;
-
- final List<github.CheckRun> checkRuns = <github.CheckRun>[];
- if (messagePullRequest.head != null && sha != null) {
- checkRuns.addAll(await gitHubService.getCheckRuns(slug, sha));
- }
-
- /// Validate if all checkRuns have succeeded.
- allSuccess = validateCheckRuns(slug, prNumber, checkRuns, failures, allSuccess);
-
- if (!allSuccess && failures.isEmpty) {
- return ValidationResult(allSuccess, Action.IGNORE_TEMPORARILY, '');
- }
-
- final StringBuffer buffer = StringBuffer();
- if (failures.isNotEmpty) {
- for (FailureDetail detail in failures) {
- buffer.writeln('- The status or check suite ${detail.markdownLink} has failed. Please fix the '
- 'issues identified (or deflake) before re-applying this label.');
- }
- }
- final Action action =
- labelNames.contains(config.overrideTreeStatusLabel) ? Action.IGNORE_FAILURE : Action.REMOVE_LABEL;
- return ValidationResult(allSuccess, action, buffer.toString());
- }
-
- /// Check the tree status.
- ///
- /// If a repo has a tree status, we should wait for it to show up instead of posting
- /// a failure to GitHub pull request.
- /// If a repo doesn't have a tree status, simply return `true`.
- bool treeStatusCheck(github.RepositorySlug slug, int prNumber, List<ContextNode> statuses) {
- bool treeStatusValid = false;
- if (!Config.reposWithTreeStatus.contains(slug)) {
- return true;
- }
- if (statuses.isEmpty) {
- return false;
- }
- const String treeStatusName = 'tree-status';
- log.info('${slug.fullName}/$prNumber: Validating tree status for ${slug.name}/tree-status, statuses: $statuses');
-
- /// Scan list of statuses to see if the tree status exists (this list is expected to be <5 items)
- for (ContextNode status in statuses) {
- if (status.context == treeStatusName) {
- // Does only one tree status need to be set for the condition?
- treeStatusValid = true;
- }
- }
- return treeStatusValid;
- }
-
- /// Validate the ci build test run statuses to see which have succeeded and
- /// which have failed.
- ///
- /// Failures will be added the set of overall failures.
- /// Returns allSuccess unmodified if there were no failures, false otherwise.
- bool validateStatuses(
- github.RepositorySlug slug,
- int prNumber,
- Author author,
- List<String> labelNames,
- List<ContextNode> statuses,
- Set<FailureDetail> failures,
- bool allSuccess,
- ) {
- final String overrideTreeStatusLabel = config.overrideTreeStatusLabel;
- log.info('Validating name: ${slug.name}/$prNumber, statuses: $statuses');
-
- for (ContextNode status in statuses) {
- // How can name be null but presumed to not be null below when added to failure?
- final String? name = status.context;
-
- // If the account author is a roller account do not block merge on flutter-gold check.
- if (config.rollerAccounts.contains(author.login!) && slug == Config.engineSlug && name == 'flutter-gold') {
- log.info('Skipping status check for flutter-gold for ${slug.fullName}/$prNumber, pr author: $author.');
- continue;
- }
-
- if (status.state != STATUS_SUCCESS) {
- if (notInAuthorsControl.contains(name) && labelNames.contains(overrideTreeStatusLabel)) {
- continue;
- }
- allSuccess = false;
- if (status.state == STATUS_FAILURE && !notInAuthorsControl.contains(name)) {
- failures.add(FailureDetail(name!, status.targetUrl!));
- }
- }
- }
-
- return allSuccess;
- }
-
- /// Validate the checkRuns to see if all have completed successfully or not.
- ///
- /// Failures will be added the set of overall failures.
- /// Returns allSuccess unmodified if there were no failures, false otherwise.
- bool validateCheckRuns(
- github.RepositorySlug slug,
- int prNumber,
- List<github.CheckRun> checkRuns,
- Set<FailureDetail> failures,
- bool allSuccess,
- ) {
- log.info('Validating name: ${slug.name}/$prNumber, checkRuns: $checkRuns');
-
- for (github.CheckRun checkRun in checkRuns) {
- final String? name = checkRun.name;
-
- if (checkRun.conclusion == github.CheckRunConclusion.skipped ||
- checkRun.conclusion == github.CheckRunConclusion.success ||
- (checkRun.status == github.CheckRunStatus.completed &&
- checkRun.conclusion == github.CheckRunConclusion.neutral)) {
- // checkrun has passed.
- continue;
- } else if (checkRun.status == github.CheckRunStatus.completed) {
- // checkrun has failed.
- log.info('${slug.name}/$prNumber: CheckRun $name failed.');
- failures.add(FailureDetail(name!, checkRun.detailsUrl as String));
- }
- allSuccess = false;
- }
-
- return allSuccess;
- }
-}
diff --git a/auto_submit/lib/validations/empty_checks.dart b/auto_submit/lib/validations/empty_checks.dart
deleted file mode 100644
index 4aa9e5c..0000000
--- a/auto_submit/lib/validations/empty_checks.dart
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2022 The Flutter 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 'package:auto_submit/model/auto_submit_query_result.dart';
-import 'package:auto_submit/validations/validation.dart';
-import 'package:github/github.dart' as github;
-
-import '../service/github_service.dart';
-
-/// Validates that the list of checks for the PR is not empty.
-class EmptyChecks extends Validation {
- EmptyChecks({
- required super.config,
- });
-
- @override
- String get name => 'EmptyChecks';
-
- @override
-
- /// Implements the validation to verify the list of checks is not empty.
- Future<ValidationResult> validate(QueryResult result, github.PullRequest messagePullRequest) async {
- final github.RepositorySlug slug = messagePullRequest.base!.repo!.slug();
- final GithubService gitHubService = await config.createGithubService(slug);
- final PullRequest pullRequest = result.repository!.pullRequest!;
- final Commit commit = pullRequest.commits!.nodes!.single.commit!;
- final String? sha = commit.oid;
- final List<github.CheckRun> checkRuns = await gitHubService.getCheckRuns(slug, sha!);
- const String message = '- This commit has no checks. Please check that ci.yaml validation has started'
- ' and there are multiple checks. If not, try uploading an empty commit.';
- return ValidationResult(checkRuns.isNotEmpty, Action.REMOVE_LABEL, message);
- }
-}
diff --git a/auto_submit/lib/validations/mergeable.dart b/auto_submit/lib/validations/mergeable.dart
deleted file mode 100644
index 8b29c64..0000000
--- a/auto_submit/lib/validations/mergeable.dart
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2023 The Flutter 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 'package:auto_submit/model/auto_submit_query_result.dart';
-import 'package:auto_submit/validations/validation.dart';
-import 'package:github/github.dart' as github;
-import '../service/log.dart';
-
-/// Validates that a pull request is in a mergeable state.
-class Mergeable extends Validation {
- Mergeable({required super.config});
-
- @override
- String get name => 'Mergeable';
-
- @override
- Future<ValidationResult> validate(QueryResult result, github.PullRequest messagePullRequest) async {
- final int pullRequestNumber = messagePullRequest.number!;
- final github.RepositorySlug slug = messagePullRequest.base!.repo!.slug();
- final MergeableState mergeableState = result.repository!.pullRequest!.mergeable!;
-
- log.info('${slug.name}/$pullRequestNumber has mergeable state $mergeableState');
-
- switch (mergeableState) {
- case MergeableState.MERGEABLE:
- return ValidationResult(
- true,
- Action.REMOVE_LABEL,
- 'Pull request ${slug.fullName}/$pullRequestNumber is mergeable',
- );
- case MergeableState.UNKNOWN:
- return ValidationResult(
- false,
- Action.IGNORE_TEMPORARILY,
- 'Mergeability of pull request ${slug.fullName}/$pullRequestNumber could not be determined at time of merge.',
- );
- case MergeableState.CONFLICTING:
- // TODO (ricardoamador) monitor to see if we should make this the default class.
- // github documentation is poor at best.
- return ValidationResult(
- false,
- Action.REMOVE_LABEL,
- 'Pull request ${slug.fullName}/$pullRequestNumber is not in a mergeable state.',
- );
- }
- }
-}
diff --git a/auto_submit/lib/validations/required_check_runs.dart b/auto_submit/lib/validations/required_check_runs.dart
deleted file mode 100644
index 80cda81..0000000
--- a/auto_submit/lib/validations/required_check_runs.dart
+++ /dev/null
@@ -1,114 +0,0 @@
-// Copyright 2022 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// Note that we need this file because Github does not expose a field within the
-// checks that states whether or not a particular check is required or not.
-
-import 'package:auto_submit/configuration/repository_configuration.dart';
-import 'package:auto_submit/exception/retryable_exception.dart';
-import 'package:auto_submit/model/auto_submit_query_result.dart' as auto;
-import 'package:auto_submit/service/config.dart';
-import 'package:auto_submit/service/github_service.dart';
-import 'package:auto_submit/service/log.dart';
-import 'package:auto_submit/validations/validation.dart';
-import 'package:github/github.dart' as github;
-import 'package:retry/retry.dart';
-
-const String ciyamlValidation = 'ci.yaml validation';
-
-/// Required check runs are check runs noted in the autosubmit.yaml configuration.
-/// In order for a pull request to be merged any check runs specified in the
-/// required check runs must pass before the bot will merge the pull request
-/// regardless of review status.
-class RequiredCheckRuns extends Validation {
- const RequiredCheckRuns({
- required super.config,
- RetryOptions? retryOptions,
- }) : retryOptions = retryOptions ?? Config.requiredChecksRetryOptions;
-
- final RetryOptions retryOptions;
-
- Future<bool> waitForRequiredChecks({
- required github.RepositorySlug slug,
- required String sha,
- required Set<String> checkNames,
- }) async {
- final GithubService githubService = await config.createGithubService(slug);
- final List<github.CheckRun> targetCheckRuns = [];
-
- for (String checkRun in checkNames) {
- targetCheckRuns.addAll(
- await githubService.getCheckRunsFiltered(
- slug: slug,
- ref: sha,
- checkName: checkRun,
- ),
- );
- }
-
- bool checksCompleted = true;
-
- try {
- for (github.CheckRun checkRun in targetCheckRuns) {
- await retryOptions.retry(
- () async {
- await _verifyCheckRunCompleted(
- slug,
- githubService,
- checkRun,
- );
- },
- retryIf: (Exception e) => e is RetryableException,
- );
- }
- } catch (e) {
- log.warning('Required check has not completed in time. ${e.toString()}');
- checksCompleted = false;
- }
-
- return checksCompleted;
- }
-
- @override
- String get name => 'RequiredCheckRuns';
-
- @override
- Future<ValidationResult> validate(auto.QueryResult result, github.PullRequest messagePullRequest) async {
- final auto.PullRequest pullRequest = result.repository!.pullRequest!;
- final auto.Commit commit = pullRequest.commits!.nodes!.single.commit!;
- final String? sha = commit.oid;
- final github.RepositorySlug slug = messagePullRequest.base!.repo!.slug();
-
- final RepositoryConfiguration repositoryConfiguration = await config.getRepositoryConfiguration(slug);
- final Set<String> requiredCheckRuns = repositoryConfiguration.requiredCheckRunsOnRevert;
-
- final bool success = await waitForRequiredChecks(slug: slug, sha: sha!, checkNames: requiredCheckRuns);
-
- return ValidationResult(
- success,
- success ? Action.REMOVE_LABEL : Action.IGNORE_TEMPORARILY,
- success ? 'All required check runs have completed.' : 'Some of the required checks did not complete in time.',
- );
- }
-}
-
-/// Function signature that will be executed with retries.
-typedef RetryHandler = Function();
-
-/// Simple function to wait on completed checkRuns with retries.
-Future<void> _verifyCheckRunCompleted(
- github.RepositorySlug slug,
- GithubService githubService,
- github.CheckRun targetCheckRun,
-) async {
- final List<github.CheckRun> checkRuns = await githubService.getCheckRunsFiltered(
- slug: slug,
- ref: targetCheckRun.headSha!,
- checkName: targetCheckRun.name,
- );
-
- if (checkRuns.first.name != targetCheckRun.name || checkRuns.first.conclusion != github.CheckRunConclusion.success) {
- throw RetryableException('${targetCheckRun.name} has not yet completed.');
- }
-}
diff --git a/auto_submit/lib/validations/validation.dart b/auto_submit/lib/validations/validation.dart
deleted file mode 100644
index 4fb5923..0000000
--- a/auto_submit/lib/validations/validation.dart
+++ /dev/null
@@ -1,91 +0,0 @@
-// Copyright 2022 The Flutter 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 'package:auto_submit/model/auto_submit_query_result.dart';
-
-import '../service/config.dart';
-import 'package:github/github.dart' as github;
-
-/// GitHub PR state constants.
-const APPROVED_STATE = 'APPROVED';
-const CHANGES_REQUESTED_STATE = 'CHANGES_REQUESTED';
-
-/// GitHub status state.
-const STATUS_SUCCESS = 'SUCCESS';
-const STATUS_FAILURE = 'FAILURE';
-
-/// GitHub merge state.
-const UNKNOWN_MERGE_STATE = 'UNKNOWN';
-
-/// Abstract class defining the signature of the validate method used to
-/// implement PR state validations.
-abstract class Validation {
- const Validation({required this.config});
-
- final Config config;
-
- /// Returns [ValidationResult] after using a [QueryResult] and [PullRequest] to validate
- /// a given PR state.
- Future<ValidationResult> validate(QueryResult result, github.PullRequest messagePullRequest);
-
- String get name;
-}
-
-/// Enum that defines the actions to execute when a validation fails.
-enum Action {
- /// Add a comment to the PR and remove the autosubmit label.
- REMOVE_LABEL,
-
- /// Ignore the failure and continue merging the PR.
- IGNORE_FAILURE,
-
- /// Do not land the PR but do not remove the autosubmit label either. This is
- /// used for temporary states that may fix by themselves.
- IGNORE_TEMPORARILY,
-}
-
-/// Holds a result of a validation execution.
-/// TODO (ricardoamador) convert this to a record after MergeResult is merged.
-class ValidationResult {
- ValidationResult(this.result, this.action, this.message);
-
- /// True if the validation was successful and should not block landing the PR.
- bool result;
-
- /// The action to execute if the validation failed.
- Action action;
-
- /// The message to add to the PR to provide some context to the user about the
- /// executed action.
- String message;
-}
-
-/// Holds metadata about a given CI build/test failure.
-class FailureDetail {
- const FailureDetail(this.name, this.url);
-
- /// The name of the check.
- final String name;
-
- /// The url to the build where the test was executed.
- final String url;
-
- /// A link in markdown format to be added to the GitHub UI.
- String get markdownLink => '[$name]($url)';
-
- @override
-
- /// Hash implementation to support adding the results to set data structures.
- int get hashCode => 17 * 31 + name.hashCode * 31 + url.hashCode;
-
- @override
-
- /// Comparison method to simplify equality validations.
- bool operator ==(Object other) {
- if (other.runtimeType != runtimeType) {
- return false;
- }
- return other is FailureDetail && other.name == name && other.url == url;
- }
-}
diff --git a/auto_submit/lib/validations/validation_filter.dart b/auto_submit/lib/validations/validation_filter.dart
deleted file mode 100644
index f65b60e..0000000
--- a/auto_submit/lib/validations/validation_filter.dart
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2023 The Flutter 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 'package:auto_submit/configuration/repository_configuration.dart';
-import 'package:auto_submit/service/config.dart';
-import 'package:auto_submit/service/process_method.dart';
-import 'package:auto_submit/validations/approval.dart';
-import 'package:auto_submit/validations/ci_successful.dart';
-import 'package:auto_submit/validations/empty_checks.dart';
-import 'package:auto_submit/validations/mergeable.dart';
-import 'package:auto_submit/validations/required_check_runs.dart';
-import 'package:auto_submit/validations/validation.dart';
-
-/// The [ValidationFilter] allows us to pick and choose and the validations to
-/// run on a particular type of pull request.
-abstract class ValidationFilter {
- factory ValidationFilter({
- required Config config,
- required ProcessMethod processMethod,
- required RepositoryConfiguration repositoryConfiguration,
- }) {
- switch (processMethod) {
- case ProcessMethod.processAutosubmit:
- return PullRequestValidationFilter(config, repositoryConfiguration);
- case ProcessMethod.processRevert:
- return RevertRequestValidationFilter(config, repositoryConfiguration);
- default:
- throw 'No such processMethod enum value';
- }
- }
-
- Set<Validation> getValidations();
-}
-
-/// [PullRequestValidationFilter] returns a Set of validations that we run on
-/// all non revert pull requests that will be merged into the mainline branch.
-class PullRequestValidationFilter implements ValidationFilter {
- PullRequestValidationFilter(this.config, this.repositoryConfiguration);
-
- final Config config;
- final RepositoryConfiguration repositoryConfiguration;
-
- @override
- Set<Validation> getValidations() {
- final Set<Validation> validationsToRun = {};
-
- validationsToRun.add(Approval(config: config));
- // If we are running ci then we need to check the checkRuns and make sure
- // there are check runs created.
- if (repositoryConfiguration.runCi) {
- validationsToRun.add(CiSuccessful(config: config));
- validationsToRun.add(EmptyChecks(config: config));
- }
- validationsToRun.add(Mergeable(config: config));
-
- return validationsToRun;
- }
-}
-
-/// [RevertRequestValidationFilter] returns a Set of validations that we run on
-/// all revert pull requests.
-class RevertRequestValidationFilter implements ValidationFilter {
- RevertRequestValidationFilter(this.config, this.repositoryConfiguration);
-
- final Config config;
- final RepositoryConfiguration repositoryConfiguration;
-
- @override
- Set<Validation> getValidations() {
- final Set<Validation> validationsToRun = {};
-
- validationsToRun.add(Approval(config: config));
- validationsToRun.add(RequiredCheckRuns(config: config));
- validationsToRun.add(Mergeable(config: config));
-
- return validationsToRun;
- }
-}
diff --git a/auto_submit/pubspec.yaml b/auto_submit/pubspec.yaml
deleted file mode 100644
index 324b138..0000000
--- a/auto_submit/pubspec.yaml
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright 2022 The Flutter Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-name: auto_submit
-description: GitHub application for managing pull request submission
-version: 0.0.1
-publish_to: none
-homepage: https://github.com/flutter/cocoon/blob/main/autosubmit
-
-environment:
- sdk: '>=3.0.0 <4.0.0'
-
-dependencies:
- appengine: 0.13.7
- corsac_jwt: 1.0.0-nullsafety.1
- github: 9.19.0
- googleapis: 11.4.0
- googleapis_auth: 1.4.1
- graphql: 5.2.0-beta.6
- gql: 1.0.1-alpha+1691943394579
- http: 1.1.0
- json_annotation: 4.8.1
- meta: 1.11.0
- neat_cache: 2.0.3
- shelf: 1.4.1
- shelf_router: 1.1.4
- crypto: 3.0.3
- logging: 1.2.0
- retry: 3.1.2
- yaml: 3.1.2
- mutex: 3.1.0
-
-dev_dependencies:
- build_runner: 2.4.6
- json_serializable: 6.7.1
- flutter_lints: 3.0.1
- mockito: 5.4.2
- test: 1.24.9
-
-builders:
- json_serializable: 3.3.0
diff --git a/auto_submit/test/configuration/repository_configuration_data.dart b/auto_submit/test/configuration/repository_configuration_data.dart
deleted file mode 100644
index dd578b6..0000000
--- a/auto_submit/test/configuration/repository_configuration_data.dart
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2023 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-const String sampleConfigNoOverride = '''
- default_branch: main
- allow_config_override: false
- auto_approval_accounts:
- - dependabot[bot]
- - dependabot
- - DartDevtoolWorkflowBot
- approving_reviews: 2
- approval_group: flutter-hackers
- run_ci: true
- support_no_review_revert: true
- required_checkruns_on_revert:
- - ci.yaml validation
-''';
-
-const String sampleConfigRevertReviewRequired = '''
- default_branch: main
- allow_config_override: false
- auto_approval_accounts:
- - dependabot[bot]
- - dependabot
- - DartDevtoolWorkflowBot
- approving_reviews: 2
- approval_group: flutter-hackers
- run_ci: true
- support_no_review_revert: false
- required_checkruns_on_revert:
- - ci.yaml validation
-''';
-
-const String sampleConfigWithOverride = '''
- default_branch: main
- allow_config_override: true
- auto_approval_accounts:
- - dependabot[bot]
- - dependabot
- - DartDevtoolWorkflowBot
- approving_reviews: 2
- approval_group: flutter-hackers
- run_ci: true
- support_no_review_revert: true
- required_checkruns_on_revert:
- - ci.yaml validation
-''';
diff --git a/auto_submit/test/configuration/repository_configuration_manager_test.dart b/auto_submit/test/configuration/repository_configuration_manager_test.dart
deleted file mode 100644
index 37b4ce8..0000000
--- a/auto_submit/test/configuration/repository_configuration_manager_test.dart
+++ /dev/null
@@ -1,354 +0,0 @@
-// Copyright 2023 The Flutter 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 'package:auto_submit/configuration/repository_configuration.dart';
-import 'package:auto_submit/configuration/repository_configuration_manager.dart';
-import 'package:github/github.dart';
-import 'package:neat_cache/cache_provider.dart';
-import 'package:neat_cache/neat_cache.dart';
-import 'package:test/test.dart';
-
-import '../src/service/fake_config.dart';
-import '../src/service/fake_github_service.dart';
-import 'repository_configuration_data.dart';
-
-void main() {
- late RepositoryConfigurationManager repositoryConfigurationManager;
- late CacheProvider cacheProvider;
- late Cache cache;
-
- late FakeGithubService githubService;
- late FakeConfig config;
-
- setUp(() {
- cacheProvider = Cache.inMemoryCacheProvider(5);
- cache = Cache<dynamic>(cacheProvider).withPrefix('config');
- githubService = FakeGithubService();
- config = FakeConfig(githubService: githubService);
- repositoryConfigurationManager = RepositoryConfigurationManager(config, cache);
- });
-
- test('Verify cache storage', () async {
- const String sampleConfig = '''
- default_branch: main
- auto_approval_accounts:
- - dependabot[bot]
- - dependabot
- - DartDevtoolWorkflowBot
- approving_reviews: 2
- approval_group: flutter-hackers
- run_ci: true
- support_no_review_revert: true
- required_checkruns_on_revert:
- - ci.yaml validation
- - Google-testing
- - test (ubuntu-latest, 2.18.0)
- - cla/google
- ''';
-
- githubService.fileContentsMockList.add(sampleConfig);
- final RepositoryConfiguration repositoryConfiguration =
- await repositoryConfigurationManager.readRepositoryConfiguration(
- RepositorySlug('flutter', 'cocoon'),
- );
-
- expect(repositoryConfiguration.allowConfigOverride, isFalse);
- expect(repositoryConfiguration.defaultBranch, 'main');
- expect(repositoryConfiguration.autoApprovalAccounts.isNotEmpty, isTrue);
- expect(repositoryConfiguration.autoApprovalAccounts.length, 3);
- expect(repositoryConfiguration.approvingReviews, 2);
- expect(repositoryConfiguration.approvalGroup, 'flutter-hackers');
- expect(repositoryConfiguration.runCi, isTrue);
- expect(repositoryConfiguration.supportNoReviewReverts, isTrue);
- expect(repositoryConfiguration.requiredCheckRunsOnRevert.isNotEmpty, isTrue);
- expect(repositoryConfiguration.requiredCheckRunsOnRevert.length, 4);
- expect(repositoryConfiguration.requiredCheckRunsOnRevert.contains('ci.yaml validation'), isTrue);
- expect(repositoryConfiguration.requiredCheckRunsOnRevert.contains('Google-testing'), isTrue);
- expect(repositoryConfiguration.requiredCheckRunsOnRevert.contains('test (ubuntu-latest, 2.18.0)'), isTrue);
- expect(repositoryConfiguration.requiredCheckRunsOnRevert.contains('cla/google'), isTrue);
- });
-
- test('Omitted issues_repository assumes provided slug is for issues', () async {
- const String sampleConfig = '''
- default_branch: main
- auto_approval_accounts:
- - dependabot[bot]
- - dependabot
- - DartDevtoolWorkflowBot
- approving_reviews: 2
- approval_group: flutter-hackers
- run_ci: true
- support_no_review_revert: true
- required_checkruns_on_revert:
- - ci.yaml validation
- - Google-testing
- ''';
-
- githubService.fileContentsMockList.add(sampleConfig);
- final RepositoryConfiguration repositoryConfiguration =
- await repositoryConfigurationManager.readRepositoryConfiguration(
- RepositorySlug('flutter', 'cocoon'),
- );
-
- expect(repositoryConfiguration.allowConfigOverride, isFalse);
- expect(repositoryConfiguration.defaultBranch, 'main');
- expect(repositoryConfiguration.autoApprovalAccounts.isNotEmpty, isTrue);
- expect(repositoryConfiguration.autoApprovalAccounts.length, 3);
- expect(repositoryConfiguration.approvingReviews, 2);
- expect(repositoryConfiguration.approvalGroup, 'flutter-hackers');
- expect(repositoryConfiguration.runCi, isTrue);
- expect(repositoryConfiguration.supportNoReviewReverts, isTrue);
- expect(repositoryConfiguration.requiredCheckRunsOnRevert.isNotEmpty, isTrue);
- expect(repositoryConfiguration.requiredCheckRunsOnRevert.length, 2);
- expect(repositoryConfiguration.requiredCheckRunsOnRevert.contains('ci.yaml validation'), isTrue);
- expect(repositoryConfiguration.requiredCheckRunsOnRevert.contains('Google-testing'), isTrue);
- });
-
- test('Default branch collected if omitted master', () async {
- const String sampleConfig = '''
- auto_approval_accounts:
- - dependabot[bot]
- - dependabot
- - DartDevtoolWorkflowBot
- approving_reviews: 2
- approval_group: flutter-hackers
- run_ci: true
- support_no_review_revert: true
- required_checkruns_on_revert:
- - ci.yaml validation
- - Google-testing
- ''';
-
- githubService.fileContentsMockList.add(sampleConfig);
- githubService.defaultBranch = 'master';
- final RepositoryConfiguration repositoryConfiguration =
- await repositoryConfigurationManager.readRepositoryConfiguration(
- RepositorySlug('flutter', 'flutter'),
- );
-
- expect(repositoryConfiguration.allowConfigOverride, isFalse);
- expect(repositoryConfiguration.defaultBranch, 'master');
- expect(repositoryConfiguration.autoApprovalAccounts.isNotEmpty, isTrue);
- expect(repositoryConfiguration.autoApprovalAccounts.length, 3);
- expect(repositoryConfiguration.approvingReviews, 2);
- expect(repositoryConfiguration.approvalGroup, 'flutter-hackers');
- expect(repositoryConfiguration.runCi, isTrue);
- expect(repositoryConfiguration.supportNoReviewReverts, isTrue);
- expect(repositoryConfiguration.requiredCheckRunsOnRevert.isNotEmpty, isTrue);
- expect(repositoryConfiguration.requiredCheckRunsOnRevert.length, 2);
- expect(repositoryConfiguration.requiredCheckRunsOnRevert.contains('ci.yaml validation'), isTrue);
- expect(repositoryConfiguration.requiredCheckRunsOnRevert.contains('Google-testing'), isTrue);
- });
-
- test('Default branch collected if omitted main', () async {
- const String sampleConfig = '''
- auto_approval_accounts:
- - dependabot[bot]
- - dependabot
- - DartDevtoolWorkflowBot
- approving_reviews: 2
- approval_group: flutter-hackers
- run_ci: true
- support_no_review_revert: true
- required_checkruns_on_revert:
- - ci.yaml validation
- - Google-testing
- ''';
-
- githubService.fileContentsMockList.add(sampleConfig);
- githubService.defaultBranch = 'main';
- final RepositoryConfiguration repositoryConfiguration =
- await repositoryConfigurationManager.readRepositoryConfiguration(
- RepositorySlug('flutter', 'flutter'),
- );
-
- expect(repositoryConfiguration.allowConfigOverride, isFalse);
- expect(repositoryConfiguration.defaultBranch, 'main');
- expect(repositoryConfiguration.autoApprovalAccounts.isNotEmpty, isTrue);
- expect(repositoryConfiguration.autoApprovalAccounts.length, 3);
- expect(repositoryConfiguration.approvingReviews, 2);
- expect(repositoryConfiguration.approvalGroup, 'flutter-hackers');
- expect(repositoryConfiguration.runCi, isTrue);
- expect(repositoryConfiguration.supportNoReviewReverts, isTrue);
- expect(repositoryConfiguration.requiredCheckRunsOnRevert.isNotEmpty, isTrue);
- expect(repositoryConfiguration.requiredCheckRunsOnRevert.length, 2);
- expect(repositoryConfiguration.requiredCheckRunsOnRevert.contains('ci.yaml validation'), isTrue);
- expect(repositoryConfiguration.requiredCheckRunsOnRevert.contains('Google-testing'), isTrue);
- });
-
- group('Merging configurations tests', () {
- // Sample configuration being used for these tests
- /*
- default_branch: main
- allow_config_override: true
- auto_approval_accounts:
- - dependabot[bot]
- - dependabot
- - DartDevtoolWorkflowBot
- approving_reviews: 2
- approval_group: flutter-hackers
- run_ci: true
- support_no_review_revert: true
- required_checkruns_on_revert:
- - ci.yaml validation
- */
-
- test('Global config merged with default local config', () {
- final RepositoryConfiguration localRepositoryConfiguration = RepositoryConfiguration();
- final RepositoryConfiguration globalRepositoryConfiguration =
- RepositoryConfiguration.fromYaml(sampleConfigWithOverride);
- final RepositoryConfiguration mergedRepositoryConfiguration = repositoryConfigurationManager.mergeConfigurations(
- globalRepositoryConfiguration,
- localRepositoryConfiguration,
- );
- expect(mergedRepositoryConfiguration.defaultBranch, 'main');
- expect(mergedRepositoryConfiguration.allowConfigOverride, isTrue);
- expect(mergedRepositoryConfiguration.autoApprovalAccounts.length, 3);
- expect(mergedRepositoryConfiguration.autoApprovalAccounts.contains('dependabot[bot]'), isTrue);
- expect(mergedRepositoryConfiguration.autoApprovalAccounts.contains('dependabot'), isTrue);
- expect(mergedRepositoryConfiguration.autoApprovalAccounts.contains('DartDevtoolWorkflowBot'), isTrue);
- expect(mergedRepositoryConfiguration.approvingReviews, 2);
- expect(mergedRepositoryConfiguration.runCi, isTrue);
- expect(mergedRepositoryConfiguration.supportNoReviewReverts, isTrue);
- expect(mergedRepositoryConfiguration.requiredCheckRunsOnRevert.length, 1);
- expect(mergedRepositoryConfiguration.requiredCheckRunsOnRevert.contains('ci.yaml validation'), isTrue);
- });
-
- test('Auto approval accounts is additive, they cannot be removed', () {
- const String expectedAddedApprovalAccount = 'flutter-roller-account';
- const Set<String> localAutoApprovalAccounts = {expectedAddedApprovalAccount};
- final RepositoryConfiguration localRepositoryConfiguration =
- RepositoryConfiguration(autoApprovalAccounts: localAutoApprovalAccounts);
- final RepositoryConfiguration globalRepositoryConfiguration =
- RepositoryConfiguration.fromYaml(sampleConfigWithOverride);
- final RepositoryConfiguration mergedRepositoryConfiguration = repositoryConfigurationManager.mergeConfigurations(
- globalRepositoryConfiguration,
- localRepositoryConfiguration,
- );
- expect(mergedRepositoryConfiguration.autoApprovalAccounts.length, 4);
- expect(mergedRepositoryConfiguration.autoApprovalAccounts.contains('dependabot[bot]'), isTrue);
- expect(mergedRepositoryConfiguration.autoApprovalAccounts.contains('dependabot'), isTrue);
- expect(mergedRepositoryConfiguration.autoApprovalAccounts.contains('DartDevtoolWorkflowBot'), isTrue);
- expect(mergedRepositoryConfiguration.autoApprovalAccounts.contains(expectedAddedApprovalAccount), isTrue);
- });
-
- test('Duplicate auto approval account is not added', () {
- const String expectedAddedApprovalAccount = 'DartDevtoolWorkflowBot';
- const Set<String> localAutoApprovalAccounts = {expectedAddedApprovalAccount};
- final RepositoryConfiguration localRepositoryConfiguration =
- RepositoryConfiguration(autoApprovalAccounts: localAutoApprovalAccounts);
- final RepositoryConfiguration globalRepositoryConfiguration =
- RepositoryConfiguration.fromYaml(sampleConfigWithOverride);
- final RepositoryConfiguration mergedRepositoryConfiguration = repositoryConfigurationManager.mergeConfigurations(
- globalRepositoryConfiguration,
- localRepositoryConfiguration,
- );
- expect(mergedRepositoryConfiguration.autoApprovalAccounts.length, 3);
- expect(mergedRepositoryConfiguration.autoApprovalAccounts.contains('dependabot[bot]'), isTrue);
- expect(mergedRepositoryConfiguration.autoApprovalAccounts.contains('dependabot'), isTrue);
- expect(mergedRepositoryConfiguration.autoApprovalAccounts.contains('DartDevtoolWorkflowBot'), isTrue);
- });
-
- test('Approving reviews is overridden by local config', () {
- const int expectedApprovingReviews = 3;
- final RepositoryConfiguration localRepositoryConfiguration =
- RepositoryConfiguration(approvingReviews: expectedApprovingReviews);
- final RepositoryConfiguration globalRepositoryConfiguration =
- RepositoryConfiguration.fromYaml(sampleConfigWithOverride);
- final RepositoryConfiguration mergedRepositoryConfiguration = repositoryConfigurationManager.mergeConfigurations(
- globalRepositoryConfiguration,
- localRepositoryConfiguration,
- );
- expect(globalRepositoryConfiguration.approvingReviews != mergedRepositoryConfiguration.approvingReviews, isTrue);
- expect(mergedRepositoryConfiguration.approvingReviews, expectedApprovingReviews);
- });
-
- test('Approving reviews is not overridden if less than global config', () {
- const int expectedApprovingReviews = 2;
- final RepositoryConfiguration localRepositoryConfiguration = RepositoryConfiguration(approvingReviews: 1);
- final RepositoryConfiguration globalRepositoryConfiguration =
- RepositoryConfiguration.fromYaml(sampleConfigWithOverride);
- final RepositoryConfiguration mergedRepositoryConfiguration = repositoryConfigurationManager.mergeConfigurations(
- globalRepositoryConfiguration,
- localRepositoryConfiguration,
- );
- expect(globalRepositoryConfiguration.approvingReviews == mergedRepositoryConfiguration.approvingReviews, isTrue);
- expect(mergedRepositoryConfiguration.approvingReviews, expectedApprovingReviews);
- });
-
- test('Approval group is overridden if defined', () {
- const String expectedApprovalGroup = 'flutter-devs';
- final RepositoryConfiguration localRepositoryConfiguration =
- RepositoryConfiguration(approvalGroup: expectedApprovalGroup);
- final RepositoryConfiguration globalRepositoryConfiguration =
- RepositoryConfiguration.fromYaml(sampleConfigWithOverride);
- final RepositoryConfiguration mergedRepositoryConfiguration = repositoryConfigurationManager.mergeConfigurations(
- globalRepositoryConfiguration,
- localRepositoryConfiguration,
- );
- expect(mergedRepositoryConfiguration.approvalGroup, expectedApprovalGroup);
- });
-
- test('RunCi is updated if it differs from global config', () {
- const bool expectedRunCi = false;
- final RepositoryConfiguration localRepositoryConfiguration = RepositoryConfiguration(runCi: expectedRunCi);
- final RepositoryConfiguration globalRepositoryConfiguration =
- RepositoryConfiguration.fromYaml(sampleConfigWithOverride);
- final RepositoryConfiguration mergedRepositoryConfiguration = repositoryConfigurationManager.mergeConfigurations(
- globalRepositoryConfiguration,
- localRepositoryConfiguration,
- );
- expect(globalRepositoryConfiguration.runCi != mergedRepositoryConfiguration.runCi, isTrue);
- expect(mergedRepositoryConfiguration.runCi, expectedRunCi);
- });
-
- test('Support no review reverts is updated if it differs from global config', () {
- const bool expectedSupportNoReviewReverts = false;
- final RepositoryConfiguration localRepositoryConfiguration =
- RepositoryConfiguration(supportNoReviewReverts: expectedSupportNoReviewReverts);
- final RepositoryConfiguration globalRepositoryConfiguration =
- RepositoryConfiguration.fromYaml(sampleConfigWithOverride);
- final RepositoryConfiguration mergedRepositoryConfiguration = repositoryConfigurationManager.mergeConfigurations(
- globalRepositoryConfiguration,
- localRepositoryConfiguration,
- );
- expect(
- globalRepositoryConfiguration.supportNoReviewReverts != mergedRepositoryConfiguration.supportNoReviewReverts,
- isTrue,
- );
- expect(mergedRepositoryConfiguration.supportNoReviewReverts, expectedSupportNoReviewReverts);
- });
-
- test('Required check runs on revert is additive, they cannot be removed', () {
- const String expectedRequiredCheckRun = 'Linux Device Doctor Validator';
- const Set<String> localrequiredCheckRunsOnRevert = {expectedRequiredCheckRun};
- final RepositoryConfiguration localRepositoryConfiguration =
- RepositoryConfiguration(requiredCheckRunsOnRevert: localrequiredCheckRunsOnRevert);
- final RepositoryConfiguration globalRepositoryConfiguration =
- RepositoryConfiguration.fromYaml(sampleConfigWithOverride);
- final RepositoryConfiguration mergedRepositoryConfiguration = repositoryConfigurationManager.mergeConfigurations(
- globalRepositoryConfiguration,
- localRepositoryConfiguration,
- );
- expect(mergedRepositoryConfiguration.requiredCheckRunsOnRevert.length, 2);
- expect(mergedRepositoryConfiguration.requiredCheckRunsOnRevert.contains('ci.yaml validation'), isTrue);
- expect(mergedRepositoryConfiguration.requiredCheckRunsOnRevert.contains(expectedRequiredCheckRun), isTrue);
- });
-
- test('Duplicate required check run on revert is not added', () {
- const String expectedRequiredCheckRun = 'ci.yaml validation';
- const Set<String> localRequiredCheckRunsOnRevert = {expectedRequiredCheckRun};
- final RepositoryConfiguration localRepositoryConfiguration =
- RepositoryConfiguration(requiredCheckRunsOnRevert: localRequiredCheckRunsOnRevert);
- final RepositoryConfiguration globalRepositoryConfiguration =
- RepositoryConfiguration.fromYaml(sampleConfigWithOverride);
- final RepositoryConfiguration mergedRepositoryConfiguration = repositoryConfigurationManager.mergeConfigurations(
- globalRepositoryConfiguration,
- localRepositoryConfiguration,
- );
- expect(mergedRepositoryConfiguration.requiredCheckRunsOnRevert.length, 1);
- expect(mergedRepositoryConfiguration.requiredCheckRunsOnRevert.contains(expectedRequiredCheckRun), isTrue);
- });
- });
-}
diff --git a/auto_submit/test/configuration/repository_configuration_test.dart b/auto_submit/test/configuration/repository_configuration_test.dart
deleted file mode 100644
index 8277d1b..0000000
--- a/auto_submit/test/configuration/repository_configuration_test.dart
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright 2023 The Flutter 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 'package:auto_submit/configuration/repository_configuration.dart';
-import 'package:test/test.dart';
-
-void main() {
- test('Parse config from yaml', () {
- const String sampleConfig = '''
- default_branch: main
- auto_approval_accounts:
- - dependabot[bot]
- - dependabot
- - DartDevtoolWorkflowBot
- approving_reviews: 2
- approval_group: flutter-hackers
- run_ci: true
- support_no_review_revert: true
- required_checkruns_on_revert:
- - ci.yaml validation
- - Google-testing
- ''';
-
- final RepositoryConfiguration repositoryConfiguration = RepositoryConfiguration.fromYaml(sampleConfig);
- expect(repositoryConfiguration.defaultBranch, 'main');
- expect(repositoryConfiguration.autoApprovalAccounts.isNotEmpty, isTrue);
- expect(repositoryConfiguration.approvingReviews, 2);
- expect(repositoryConfiguration.runCi, isTrue);
- expect(repositoryConfiguration.supportNoReviewReverts, isTrue);
- expect(repositoryConfiguration.requiredCheckRunsOnRevert.isNotEmpty, isTrue);
- });
-
- test('Parse config from yaml excluding auto approval accounts', () {
- const String sampleConfig = '''
- default_branch: main
- approving_reviews: 2
- approval_group: flutter-hackers
- run_ci: true
- support_no_review_revert: true
- required_checkruns_on_revert:
- - “ci.yaml validation”
- - “Google-testing”
- ''';
-
- final RepositoryConfiguration repositoryConfiguration = RepositoryConfiguration.fromYaml(sampleConfig);
- expect(repositoryConfiguration.defaultBranch, 'main');
- expect(repositoryConfiguration.autoApprovalAccounts.isEmpty, isTrue);
- expect(repositoryConfiguration.approvingReviews, 2);
- expect(repositoryConfiguration.runCi, isTrue);
- expect(repositoryConfiguration.supportNoReviewReverts, isTrue);
- expect(repositoryConfiguration.requiredCheckRunsOnRevert.isNotEmpty, isTrue);
- });
-
- test('Parse config from yaml with empty auto_approval_accounts field', () {
- const String sampleConfig = '''
- auto_approval_accounts:
- approving_reviews: 2
- approval_group: flutter-hackers
- run_ci: true
- support_no_review_revert: true
- required_checkruns_on_revert:
- - “ci.yaml validation”
- - “Google-testing”
- ''';
-
- final RepositoryConfiguration repositoryConfiguration = RepositoryConfiguration.fromYaml(sampleConfig);
- // We will get the default branch later as it does not need to be added to
- // the initial configuration.
- repositoryConfiguration.defaultBranch = 'main';
-
- expect(repositoryConfiguration.allowConfigOverride, false);
- expect(repositoryConfiguration.defaultBranch, 'main');
- expect(repositoryConfiguration.autoApprovalAccounts.isEmpty, isTrue);
- expect(repositoryConfiguration.approvingReviews, 2);
- expect(repositoryConfiguration.runCi, isTrue);
- expect(repositoryConfiguration.supportNoReviewReverts, isTrue);
- expect(repositoryConfiguration.requiredCheckRunsOnRevert.isNotEmpty, isTrue);
- });
-
- test('Parse minimal configuration', () {
- const String sampleConfig = '''
- approval_group: flutter-hackers
- issues_repository:
- owner: flutter
- repo: flutter
- ''';
-
- final RepositoryConfiguration repositoryConfiguration = RepositoryConfiguration.fromYaml(sampleConfig);
- repositoryConfiguration.defaultBranch = 'master';
-
- expect(repositoryConfiguration.defaultBranch, 'master');
- expect(repositoryConfiguration.autoApprovalAccounts.isEmpty, isTrue);
- expect(repositoryConfiguration.approvingReviews, 2);
- expect(repositoryConfiguration.runCi, isTrue);
- expect(repositoryConfiguration.supportNoReviewReverts, isTrue);
- expect(repositoryConfiguration.requiredCheckRunsOnRevert.isEmpty, isTrue);
- });
-}
diff --git a/auto_submit/test/git/cli_command_test.dart b/auto_submit/test/git/cli_command_test.dart
deleted file mode 100644
index 471c53c..0000000
--- a/auto_submit/test/git/cli_command_test.dart
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2023 The Flutter 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:auto_submit/git/cli_command.dart';
-import 'package:test/test.dart';
-
-void main() {
- group('Testing git command locally', () {
- test('Checkout locally.', () async {
- String executable = 'ls';
- if (Platform.isWindows) {
- executable = 'dir';
- }
-
- final CliCommand cliCommand = CliCommand();
- final ProcessResult processResult = await cliCommand.runCliCommand(
- executable: executable,
- arguments: [],
- );
- expect(processResult.exitCode, isZero);
- });
- });
-}
diff --git a/auto_submit/test/git/git_cli_test.dart b/auto_submit/test/git/git_cli_test.dart
deleted file mode 100644
index 234799b..0000000
--- a/auto_submit/test/git/git_cli_test.dart
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2023 The Flutter 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:auto_submit/git/cli_command.dart';
-import 'package:auto_submit/git/utilities.dart';
-import 'package:auto_submit/git/git_cli.dart';
-import 'package:github/github.dart';
-
-import 'package:test/test.dart';
-
-void main() {
- group('Testing git commands', () {
- late CliCommand cliCommand;
- late GitCli gitCli;
- final String workingDirectory = '${Directory.current.path}/test/repository';
- final String fullRepoCheckoutPath = '$workingDirectory/test_repo';
- final RepositorySlug slug = RepositorySlug('flutter', 'test_repo');
-
- late ProcessResult initProcessResult;
-
- setUp(() async {
- cliCommand = CliCommand();
- final Directory directory = Directory(workingDirectory);
- directory.createSync();
- gitCli = GitCli(GitAccessMethod.SSH, cliCommand);
- initProcessResult = await cliCommand.runCliCommand(
- executable: 'git',
- arguments: [
- 'init',
- slug.name,
- '-b',
- 'main',
- ],
- workingDirectory: workingDirectory,
- throwOnError: false,
- );
- });
-
- void validateInit() {
- expect(initProcessResult, isNotNull);
- expect(initProcessResult.exitCode, isZero);
- expect(Directory(fullRepoCheckoutPath).existsSync(), isTrue);
- }
-
- test('isGitRepository()', () async {
- validateInit();
- expect(await gitCli.isGitRepository(fullRepoCheckoutPath), isTrue);
- });
-
- test('createBranch()', () async {
- validateInit();
-
- final ProcessResult branchProcessResult = await gitCli.createBranch(
- newBranchName: 'test_branch',
- workingDirectory: fullRepoCheckoutPath,
- useCheckout: true,
- );
- expect(branchProcessResult, isNotNull);
- expect(branchProcessResult.exitCode, isZero);
-
- final ProcessResult processResult = await cliCommand.runCliCommand(
- executable: 'git',
- arguments: ['status'],
- workingDirectory: fullRepoCheckoutPath,
- );
- expect((processResult.stdout as String).contains('On branch test_branch'), isTrue);
- });
-
- tearDown(() async {
- await cliCommand.runCliCommand(
- executable: 'rm',
- arguments: ['-rf', fullRepoCheckoutPath],
- );
- });
- });
-}
diff --git a/auto_submit/test/git/git_repository_manager_test.dart b/auto_submit/test/git/git_repository_manager_test.dart
deleted file mode 100644
index ca85b25..0000000
--- a/auto_submit/test/git/git_repository_manager_test.dart
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright 2023 The Flutter 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:auto_submit/git/cli_command.dart';
-import 'package:auto_submit/git/utilities.dart';
-import 'package:auto_submit/git/git_cli.dart';
-import 'package:auto_submit/git/git_repository_manager.dart';
-import 'package:github/github.dart';
-
-import 'package:test/test.dart';
-
-void main() {
- group(
- 'RepositoryManager',
- () {
- final String workingDirectoryOutside = Directory.current.parent.parent.path;
-
- final String workingDirectory = '${Directory.current.path}/test/repository';
- final String targetRepoCheckoutDirectory = '${Directory.current.path}/test/repository/flutter_test';
- final CliCommand cliCommand = CliCommand();
- final GitCli gitCli = GitCli(GitAccessMethod.SSH, cliCommand);
- final RepositorySlug slug = RepositorySlug('ricardoamador', 'flutter_test');
-
- final GitRepositoryManager gitRepositoryManager = GitRepositoryManager(
- slug: slug,
- workingDirectory: workingDirectory,
- cloneToDirectory: 'flutter_test',
- gitCli: gitCli,
- );
-
- setUp(() {
- final Directory directory = Directory(workingDirectory);
- directory.createSync();
- });
-
- test('cloneRepository()', () async {
- await gitRepositoryManager.cloneRepository();
- expect(Directory(targetRepoCheckoutDirectory).existsSync(), isTrue);
- });
-
- test('cloneRepository() over existing dir.', () async {
- await cliCommand.runCliCommand(executable: 'mkdir', arguments: ['$workingDirectory/flutter_test']);
- await gitRepositoryManager.cloneRepository();
- expect(Directory('$workingDirectoryOutside/flutter_test').existsSync(), isTrue);
- expect(await gitCli.isGitRepository('$workingDirectoryOutside/flutter_test'), isTrue);
- });
-
- test('deleteRepository()', () async {
- await gitRepositoryManager.cloneRepository();
- expect(Directory(targetRepoCheckoutDirectory).existsSync(), isTrue);
- await gitRepositoryManager.deleteRepository();
- expect(Directory(targetRepoCheckoutDirectory).existsSync(), isFalse);
- });
-
- tearDown(() async {
- await cliCommand.runCliCommand(executable: 'rm', arguments: ['-rf', targetRepoCheckoutDirectory]);
- });
- },
- skip: true,
- );
-}
diff --git a/auto_submit/test/model/auto_submit_query_result_test.dart b/auto_submit/test/model/auto_submit_query_result_test.dart
deleted file mode 100644
index fa440d9..0000000
--- a/auto_submit/test/model/auto_submit_query_result_test.dart
+++ /dev/null
@@ -1,178 +0,0 @@
-// Copyright 2022 The Flutter 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:convert';
-
-import 'package:auto_submit/model/auto_submit_query_result.dart';
-import 'package:test/test.dart';
-
-void main() {
- late QueryResult queryResult;
- late RevertPullRequestData revertPullRequestData;
-
- group('Auto Submit Models', () {
- setUp(() {
- queryResult = QueryResult.fromJson(data);
- });
-
- test('repository values', () async {
- final Repository repository = queryResult.repository!;
- expect(repository.pullRequest, isNotNull);
- });
-
- test('pullRequest values', () async {
- final PullRequest pullRequest = queryResult.repository!.pullRequest!;
- expect(pullRequest.author, isNotNull);
- expect(pullRequest.authorAssociation, 'MEMBER');
- expect(pullRequest.commits, isNotNull);
- expect(pullRequest.id, 'PR_kwDOA8VHis43rs4_');
- expect(pullRequest.reviews, isNotNull);
- expect(pullRequest.title, '[dependabot] Remove human reviewers');
- expect(pullRequest.mergeable, MergeableState.MERGEABLE);
- });
-
- test('Author values', () async {
- final Author author = queryResult.repository!.pullRequest!.author!;
- expect(author.login, 'author1');
- });
-
- test('Reviews values', () async {
- final Reviews reviews = queryResult.repository!.pullRequest!.reviews!;
- expect(reviews.nodes, isNotNull);
- expect(reviews.nodes!.single, isA<ReviewNode>());
- final ReviewNode review = reviews.nodes!.single;
- expect(review.author, isA<Author>());
- expect(review.authorAssociation, 'MEMBER');
- expect(review.state, 'APPROVED');
- });
-
- test('Commits values', () async {
- final Commits commits = queryResult.repository!.pullRequest!.commits!;
- expect(commits.nodes, isNotNull);
- final CommitNode commitNode = commits.nodes!.first;
- expect(commitNode.commit, isNotNull);
- expect(commitNode.commit!.abbreviatedOid, '4009ecc');
- expect(commitNode.commit!.oid, '4009ecc0b6dbf5cb19cb97472147063e7368ec10');
- expect(commitNode.commit!.pushedDate, DateTime.parse('2022-05-11 22:35:03.000Z'));
- expect(commitNode.commit!.status, isNull);
- });
- });
-
- group('Revert pull request models', () {
- setUp(() {
- revertPullRequestData = RevertPullRequestData.fromJson(revertData);
- });
-
- test('All fields are present', () {
- expect(revertPullRequestData.revertPullRequest, isNotNull);
- expect(revertPullRequestData.revertPullRequest!.clientMutationId, isNotNull);
- expect(revertPullRequestData.revertPullRequest!.pullRequest, isNotNull);
- expect(revertPullRequestData.revertPullRequest!.revertPullRequest, isNotNull);
- });
-
- test('Client Mutation Id field', () {
- expect(revertPullRequestData.revertPullRequest!.clientMutationId, 'ra186026');
- });
-
- test('To be reverted PullRequest field.', () {
- final PullRequest pullRequest = revertPullRequestData.revertPullRequest!.pullRequest!;
- expect(pullRequest.id, 'PR_kwDOIRxr_M5MQ7mV');
- expect(pullRequest.title, 'Adding a TODO comment for testing pull request auto approval.');
- expect(pullRequest.author!.login, 'ricardoamador');
- expect(pullRequest.body, 'This is for testing revert and should be present in the revert mutation.');
- });
-
- test('Revert PullRequest field.', () {
- final PullRequest revertPullRequest = revertPullRequestData.revertPullRequest!.revertPullRequest!;
- expect(revertPullRequest.id, 'PR_kwDOIRxr_M5QN0kD');
- expect(revertPullRequest.title, 'Revert comment in configuration file.');
- expect(revertPullRequest.author!.login, 'ricardoamador');
- expect(revertPullRequest.body, 'Testing revert mutation');
- });
- });
-}
-
-final Map<String, dynamic> data = json.decode(dataString) as Map<String, dynamic>;
-
-const String dataString = '''
-{
- "repository": {
- "pullRequest": {
- "author": {
- "login": "author1"
- },
- "authorAssociation": "MEMBER",
- "id": "PR_kwDOA8VHis43rs4_",
- "title": "[dependabot] Remove human reviewers",
- "mergeable": "MERGEABLE",
- "commits": {
- "nodes":[
- {
- "commit": {
- "abbreviatedOid": "4009ecc",
- "oid": "4009ecc0b6dbf5cb19cb97472147063e7368ec10",
- "committedDate": "2022-05-11T22:35:02Z",
- "pushedDate": "2022-05-11T22:35:03Z",
- "status":null
- }
- }
- ]
- },
- "reviews": {
- "nodes": [
- {
- "author": {
- "login": "keyonghan"
- },
- "authorAssociation": "MEMBER",
- "state": "APPROVED"
- }
- ]
- }
- }
- }
-}
-''';
-
-final Map<String, dynamic> revertData = json.decode(revertRequestString) as Map<String, dynamic>;
-
-const String revertRequestString = '''
-{
- "revertPullRequest": {
- "clientMutationId": "ra186026",
- "pullRequest": {
- "author": {
- "login": "ricardoamador"
- },
- "authorAssociation": "OWNER",
- "id": "PR_kwDOIRxr_M5MQ7mV",
- "title": "Adding a TODO comment for testing pull request auto approval.",
- "number": 18,
- "body": "This is for testing revert and should be present in the revert mutation.",
- "repository": {
- "owner": {
- "login": "ricardoamador"
- },
- "name": "flutter_test"
- }
- },
- "revertPullRequest": {
- "author": {
- "login": "ricardoamador"
- },
- "authorAssociation": "OWNER",
- "id": "PR_kwDOIRxr_M5QN0kD",
- "title": "Revert comment in configuration file.",
- "number": 23,
- "body": "Testing revert mutation",
- "repository": {
- "owner": {
- "login": "ricardoamador"
- },
- "name": "flutter_test"
- }
- }
- }
-}
-''';
diff --git a/auto_submit/test/model/pull_request_change_type.dart b/auto_submit/test/model/pull_request_change_type.dart
deleted file mode 100644
index 2ee29ff..0000000
--- a/auto_submit/test/model/pull_request_change_type.dart
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2022 The Flutter 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 'package:auto_submit/model/pull_request_data_types.dart';
-import 'package:test/test.dart';
-
-void main() {
- final List<String> expectedNames = ['change', 'revert'];
-
- test('Expected string value for enum is returned.', () {
- for (PullRequestChangeType prChangeType in PullRequestChangeType.values) {
- assert(expectedNames.contains(prChangeType.name));
- }
- expect(PullRequestChangeType.values.length, 2);
- });
-}
diff --git a/auto_submit/test/request_handling/authentication_test.dart b/auto_submit/test/request_handling/authentication_test.dart
deleted file mode 100644
index 93cbbd5..0000000
--- a/auto_submit/test/request_handling/authentication_test.dart
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:auto_submit/request_handling/authentication.dart';
-import 'package:auto_submit/requests/exceptions.dart';
-import 'package:test/test.dart';
-import 'package:shelf/shelf.dart';
-
-void main() {
- group('CronAuthProvider', () {
- late Request request;
- late CronAuthProvider auth;
-
- setUp(() {
- auth = const CronAuthProvider();
- });
-
- test('throws Unauthenticated with no auth headers', () async {
- request = Request('POST', Uri.parse('http://localhost/'));
- expect(auth.authenticate(request), throwsA(isA<Unauthenticated>()));
- });
-
- test('succeeds for App Engine cronjobs', () async {
- final Map<String, String> header = {'X-Appengine-Cron': 'true'};
- request = Request('POST', Uri.parse('http://localhost/'), headers: header);
- final bool result = await auth.authenticate(request);
- expect(result, true);
- });
- });
-}
diff --git a/auto_submit/test/requests/check_pull_request_test.dart b/auto_submit/test/requests/check_pull_request_test.dart
deleted file mode 100644
index 5fed85a..0000000
--- a/auto_submit/test/requests/check_pull_request_test.dart
+++ /dev/null
@@ -1,810 +0,0 @@
-// Copyright 2022 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-// ignore_for_file: constant_identifier_names
-import 'dart:async';
-import 'dart:convert';
-import 'package:auto_submit/configuration/repository_configuration.dart';
-import 'package:auto_submit/service/config.dart';
-
-import 'package:auto_submit/requests/check_pull_request.dart';
-import 'package:auto_submit/requests/graphql_queries.dart';
-import 'package:auto_submit/service/log.dart';
-import 'package:github/github.dart';
-import 'package:googleapis/bigquery/v2.dart';
-import 'package:googleapis/pubsub/v1.dart' as pub;
-import 'package:graphql/client.dart' hide Request, Response;
-import 'package:logging/logging.dart';
-import 'package:mockito/mockito.dart';
-import 'package:test/test.dart';
-
-import '../configuration/repository_configuration_data.dart';
-import '../service/bigquery_test.dart';
-import '../src/service/fake_bigquery_service.dart';
-import './github_webhook_test_data.dart';
-import '../src/request_handling/fake_pubsub.dart';
-import '../src/request_handling/fake_authentication.dart';
-import '../src/service/fake_config.dart';
-import '../src/service/fake_github_service.dart';
-import '../src/service/fake_graphql_client.dart';
-import '../utilities/mocks.dart';
-import '../utilities/utils.dart' hide createQueryResult;
-
-const String oid = '6dcb09b5b57875f334f61aebed695e2e4193db5e';
-const String title = 'some_title';
-
-void main() {
- group('Check CheckPullRequest', () {
- late CheckPullRequest checkPullRequest;
- late FakeConfig config;
- late FakeCronAuthProvider auth;
- late FakeGraphQLClient githubGraphQLClient;
- late FakeGithubService githubService;
- late MockJobsResource jobsResource;
- late FakeBigqueryService bigqueryService;
- late MockPullRequestsService pullRequests;
- final MockGitHub gitHub = MockGitHub();
- late FakePubSub pubsub;
- late PullRequestHelper flutterRequest;
- late PullRequestHelper cocoonRequest;
- late List<QueryOptions> expectedOptions;
- late QueryOptions flutterOption;
- late QueryOptions cocoonOption;
- const String testSubscription = 'test-sub';
- const String testTopic = 'test-topic';
- const String rollorAuthor = 'engine-flutter-autoroll';
- const String labelName = 'warning: land on red to fix tree breakage';
- const String cocoonRepo = 'cocoon';
- const String noAutosubmitLabel = 'no_autosubmit';
-
- setUp(() {
- githubGraphQLClient = FakeGraphQLClient();
- auth = FakeCronAuthProvider();
- pubsub = FakePubSub();
- expectedOptions = <QueryOptions>[];
- githubService = FakeGithubService();
-
- githubGraphQLClient.mutateResultForOptions = (MutationOptions options) => createFakeQueryResult();
-
- githubGraphQLClient.queryResultForOptions = (QueryOptions options) {
- expect(options.variables['sOwner'], 'flutter');
- final String? repoName = options.variables['sName'] as String?;
- if (repoName == 'flutter') {
- return createQueryResult(flutterRequest);
- } else if (repoName == 'cocoon') {
- return createQueryResult(cocoonRequest);
- } else {
- fail('unexpected repo $repoName');
- }
- };
-
- final FindPullRequestsWithReviewsQuery findPullRequestsWithReviewsQueryFlutter = FindPullRequestsWithReviewsQuery(
- repositoryOwner: 'flutter',
- repositoryName: 'flutter',
- pullRequestNumber: 0,
- );
-
- flutterOption = QueryOptions(
- document: findPullRequestsWithReviewsQueryFlutter.documentNode,
- fetchPolicy: FetchPolicy.noCache,
- variables: findPullRequestsWithReviewsQueryFlutter.variables,
- );
-
- final FindPullRequestsWithReviewsQuery findPullRequestsWithReviewsQueryCocoon = FindPullRequestsWithReviewsQuery(
- repositoryOwner: 'flutter',
- repositoryName: 'cocoon',
- pullRequestNumber: 1,
- );
-
- cocoonOption = QueryOptions(
- document: findPullRequestsWithReviewsQueryCocoon.documentNode,
- fetchPolicy: FetchPolicy.noCache,
- variables: findPullRequestsWithReviewsQueryCocoon.variables,
- );
-
- githubService.checkRunsData = checkRunsMock;
- githubService.compareTwoCommitsData = compareTwoCommitsMock;
- githubService.successMergeData = successMergeMock;
- githubService.createCommentData = createCommentMock;
- githubService.commitData = commitMock;
- jobsResource = MockJobsResource();
- bigqueryService = FakeBigqueryService(jobsResource);
- config = FakeConfig(
- githubService: githubService,
- githubGraphQLClient: githubGraphQLClient,
- githubClient: gitHub,
- );
- config.bigqueryService = bigqueryService;
- config.repositoryConfigurationMock = RepositoryConfiguration.fromYaml(sampleConfigNoOverride);
- pullRequests = MockPullRequestsService();
- when(gitHub.pullRequests).thenReturn(pullRequests);
- when(pullRequests.get(any, any)).thenAnswer(
- (_) async => PullRequest(
- number: 123,
- state: 'open',
- ),
- );
-
- when(jobsResource.query(captureAny, any)).thenAnswer((Invocation invocation) {
- return Future<QueryResponse>.value(
- QueryResponse.fromJson(jsonDecode(insertDeleteUpdateSuccessResponse) as Map<dynamic, dynamic>),
- );
- });
- });
-
- void verifyQueries(List<QueryOptions> expectedOptions) {
- githubGraphQLClient.verifyQueries(expectedOptions);
- }
-
- test('Multiple identical messages are processed once', () async {
- final PullRequest pullRequest1 = generatePullRequest(
- prNumber: 0,
- repoName: cocoonRepo,
- );
- // 'member' is in the review nodes and 'author1' is the pr author.
- githubService.isTeamMemberMockMap['member'] = true;
- githubService.isTeamMemberMockMap['author1'] = true;
- githubService.pullRequestData = pullRequest1;
- for (int i = 0; i < 2; i++) {
- unawaited(pubsub.publish('auto-submit-queue-sub', pullRequest1));
- }
-
- checkPullRequest = CheckPullRequest(
- config: config,
- pubsub: pubsub,
- cronAuthProvider: auth,
- );
- cocoonRequest = PullRequestHelper(
- prNumber: 0,
- lastCommitHash: oid,
- );
-
- final Map<int, RepositorySlug> expectedMergeRequestMap = {};
- expectedMergeRequestMap[0] = RepositorySlug('flutter', cocoonRepo);
-
- await checkPullRequest.get();
-
- githubService.verifyMergePullRequests(expectedMergeRequestMap);
-
- expect(0, pubsub.messagesQueue.length);
- });
-
- test('Closed PRs are not processed', () async {
- final PullRequest pullRequest1 = generatePullRequest(
- prNumber: 0,
- repoName: cocoonRepo,
- state: 'close',
- );
- githubService.pullRequestData = pullRequest1;
- when(pullRequests.get(any, any)).thenAnswer(
- (_) async => PullRequest(
- number: 0,
- state: 'close',
- ),
- );
- for (int i = 0; i < 2; i++) {
- unawaited(pubsub.publish('auto-submit-queue-sub', pullRequest1));
- }
-
- checkPullRequest = CheckPullRequest(
- config: config,
- pubsub: pubsub,
- cronAuthProvider: auth,
- );
- cocoonRequest = PullRequestHelper(
- prNumber: 0,
- lastCommitHash: oid,
- );
- await checkPullRequest.get();
-
- githubGraphQLClient.verifyMutations(
- <MutationOptions>[],
- );
- expect(0, pubsub.messagesQueue.length);
- });
-
- test('Merge exception is handled correctly', () async {
- final PullRequest pullRequest1 = generatePullRequest(prNumber: 0);
- final PullRequest pullRequest2 = generatePullRequest(
- prNumber: 1,
- repoName: cocoonRepo,
- );
-
- githubService.pullRequestData = pullRequest1;
- // 'member' is in the review nodes and 'author1' is the pr author.
- githubService.isTeamMemberMockMap['member'] = true;
- githubService.isTeamMemberMockMap['author1'] = true;
-
- final List<PullRequest> pullRequests = <PullRequest>[pullRequest1, pullRequest2];
- for (PullRequest pr in pullRequests) {
- unawaited(pubsub.publish(testTopic, pr));
- }
-
- checkPullRequest = CheckPullRequest(
- config: config,
- pubsub: pubsub,
- cronAuthProvider: auth,
- );
- flutterRequest = PullRequestHelper(
- prNumber: 0,
- lastCommitHash: oid,
- );
- cocoonRequest = PullRequestHelper(
- prNumber: 1,
- lastCommitHash: oid,
- );
-
- githubService.useMergeRequestMockList = true;
- githubService.pullRequestMergeMockList.add(
- PullRequestMerge(
- merged: false,
- message: 'Unable to merge pull request',
- ),
- );
- githubService.pullRequestMergeMockList.add(
- PullRequestMerge(
- merged: true,
- sha: 'sha',
- message: 'Pull request merged successfully',
- ),
- );
-
- final Map<int, RepositorySlug> expectedMergeRequestMap = {};
- expectedMergeRequestMap[0] = RepositorySlug(
- 'flutter',
- 'flutter',
- );
- expectedMergeRequestMap[1] = RepositorySlug(
- 'flutter',
- cocoonRepo,
- );
-
- final List<LogRecord> records = <LogRecord>[];
- log.onRecord.listen((LogRecord record) => records.add(record));
- // this is the test.
- await checkPullRequest.get();
- // every failure is now acknowledged from the queue.
- expect(pubsub.messagesQueue.length, 0);
- final List<LogRecord> errorLogs = records.where((LogRecord record) => record.level == Level.SEVERE).toList();
- expect(errorLogs.length, 1);
- expect(errorLogs[0].message.contains('Failed to merge'), true);
- pubsub.messagesQueue.clear();
- });
-
- test('Merges PR with successful status and checks', () async {
- final PullRequest pullRequest1 = generatePullRequest(prNumber: 0);
- final PullRequest pullRequest2 = generatePullRequest(
- prNumber: 1,
- repoName: cocoonRepo,
- );
- githubService.pullRequestData = pullRequest1;
- // 'octocat' is the pr author from generatePullRequest calls.
- // 'member' is in the review nodes and 'author1' is the pr author.
- githubService.isTeamMemberMockMap['member'] = true;
- githubService.isTeamMemberMockMap['author1'] = true;
- final List<PullRequest> pullRequests = <PullRequest>[pullRequest1, pullRequest2];
- for (PullRequest pr in pullRequests) {
- unawaited(pubsub.publish(testTopic, pr));
- }
-
- checkPullRequest = CheckPullRequest(
- config: config,
- pubsub: pubsub,
- cronAuthProvider: auth,
- );
-
- flutterRequest = PullRequestHelper(
- prNumber: 0,
- lastCommitHash: oid,
- );
- cocoonRequest = PullRequestHelper(
- prNumber: 1,
- lastCommitHash: oid,
- );
-
- await checkPullRequest.get();
- expectedOptions.add(flutterOption);
- expectedOptions.add(cocoonOption);
- verifyQueries(expectedOptions);
-
- githubService.useMergeRequestMockList = true;
- githubService.pullRequestMergeMockList.add(
- PullRequestMerge(
- merged: true,
- sha: 'sha1',
- message: 'Pull request merged successfully',
- ),
- );
- githubService.pullRequestMergeMockList.add(
- PullRequestMerge(
- merged: true,
- sha: 'sha2',
- message: 'Pull request merged successfully',
- ),
- );
-
- final Map<int, RepositorySlug> expectedMergeRequestMap = {};
- expectedMergeRequestMap[0] = RepositorySlug('flutter', 'flutter');
- expectedMergeRequestMap[1] = RepositorySlug('flutter', cocoonRepo);
-
- githubService.verifyMergePullRequests(expectedMergeRequestMap);
-
- assert(pubsub.messagesQueue.isEmpty);
- });
-
- test('Merges unapproved PR from autoroller', () async {
- final PullRequest pullRequest = generatePullRequest(
- prNumber: 0,
- author: rollorAuthor,
- );
- githubService.pullRequestData = pullRequest;
- unawaited(pubsub.publish(testTopic, pullRequest));
-
- checkPullRequest = CheckPullRequest(
- config: config,
- pubsub: pubsub,
- cronAuthProvider: auth,
- approverProvider: (Config config) => MockApproverService(),
- );
-
- flutterRequest = PullRequestHelper(
- prNumber: 0,
- author: 'dependabot',
- reviews: const <PullRequestReviewHelper>[],
- lastCommitHash: oid,
- );
-
- await checkPullRequest.get();
- expectedOptions.add(flutterOption);
- verifyQueries(expectedOptions);
-
- final Map<int, RepositorySlug> expectedMergeRequestMap = {};
- expectedMergeRequestMap[0] = RepositorySlug(
- 'flutter',
- 'flutter',
- );
-
- githubService.mergeRequestMock = PullRequestMerge(
- merged: true,
- sha: 'sha1',
- message: 'Pull request merged successfully',
- );
-
- githubService.verifyMergePullRequests(expectedMergeRequestMap);
-
- assert(pubsub.messagesQueue.isEmpty);
- });
-
- test('Merges PR with failed tree status if override tree status label is provided', () async {
- final PullRequest pullRequest = generatePullRequest(
- prNumber: 0,
- labelName: labelName,
- );
- // 'member' is in the review nodes and 'author1' is the pr author.
- githubService.isTeamMemberMockMap['member'] = true;
- githubService.isTeamMemberMockMap['author1'] = true;
- githubService.pullRequestData = pullRequest;
- unawaited(
- pubsub.publish(
- testTopic,
- pullRequest,
- ),
- );
-
- checkPullRequest = CheckPullRequest(
- config: config,
- pubsub: pubsub,
- cronAuthProvider: auth,
- );
-
- flutterRequest = PullRequestHelper(
- prNumber: 0,
- lastCommitHash: oid,
- lastCommitStatuses: const <StatusHelper>[
- StatusHelper.flutterBuildFailure,
- ],
- );
-
- await checkPullRequest.get();
- expectedOptions.add(flutterOption);
- verifyQueries(expectedOptions);
-
- final Map<int, RepositorySlug> expectedMergeRequestMap = {};
- expectedMergeRequestMap[0] = RepositorySlug(
- 'flutter',
- 'flutter',
- );
-
- githubService.mergeRequestMock = PullRequestMerge(
- merged: true,
- sha: 'sha1',
- message: 'Pull request merged successfully',
- );
-
- githubService.verifyMergePullRequests(expectedMergeRequestMap);
-
- assert(pubsub.messagesQueue.isEmpty);
- });
-
- test('Merges a clean revert PR with in progress tests', () async {
- final PullRequest pullRequest = generatePullRequest(prNumber: 0);
- githubService.pullRequestData = pullRequest;
- // 'member' is in the review nodes and 'author1' is the pr author.
- githubService.isTeamMemberMockMap['member'] = true;
- githubService.isTeamMemberMockMap['author1'] = true;
- unawaited(pubsub.publish(testTopic, pullRequest));
- checkPullRequest = CheckPullRequest(
- config: config,
- pubsub: pubsub,
- cronAuthProvider: auth,
- );
-
- flutterRequest = PullRequestHelper(
- prNumber: 0,
- lastCommitHash: oid,
- lastCommitStatuses: const <StatusHelper>[
- StatusHelper.flutterBuildSuccess,
- ],
- lastCommitMessage: 'Revert "This is a test PR" This reverts commit abc.',
- );
-
- await checkPullRequest.get();
- expectedOptions.add(flutterOption);
- verifyQueries(expectedOptions);
-
- final Map<int, RepositorySlug> expectedMergeRequestMap = {};
- expectedMergeRequestMap[0] = RepositorySlug(
- 'flutter',
- 'flutter',
- );
-
- githubService.mergeRequestMock = PullRequestMerge(
- merged: true,
- sha: 'sha1',
- message: 'Pull request merged successfully',
- );
-
- githubService.verifyMergePullRequests(expectedMergeRequestMap);
-
- assert(pubsub.messagesQueue.isEmpty);
- });
-
- test('Merges PR with successful checks on repo without tree status', () async {
- final PullRequest pullRequest = generatePullRequest(
- prNumber: 1,
- repoName: cocoonRepo,
- );
- githubService.pullRequestData = pullRequest;
- // 'member' is in the review nodes and 'author1' is the pr author.
- githubService.isTeamMemberMockMap['member'] = true;
- githubService.isTeamMemberMockMap['author1'] = true;
- unawaited(pubsub.publish(testTopic, pullRequest));
-
- checkPullRequest = CheckPullRequest(
- config: config,
- pubsub: pubsub,
- cronAuthProvider: auth,
- );
-
- cocoonRequest = PullRequestHelper(
- lastCommitHash: oid,
- lastCommitStatuses: const <StatusHelper>[],
- );
-
- await checkPullRequest.get();
- expectedOptions.add(cocoonOption);
- verifyQueries(expectedOptions);
-
- final Map<int, RepositorySlug> expectedMergeRequestMap = {};
- expectedMergeRequestMap[1] = RepositorySlug(
- 'flutter',
- cocoonRepo,
- );
-
- githubService.mergeRequestMock = PullRequestMerge(
- merged: true,
- sha: 'sha1',
- message: 'Pull request merged successfully',
- );
-
- githubService.verifyMergePullRequests(expectedMergeRequestMap);
-
- assert(pubsub.messagesQueue.isEmpty);
- });
-
- test('Merges PR with neutral status checkrun', () async {
- final PullRequest pullRequest1 = generatePullRequest(prNumber: 0);
- final PullRequest pullRequest2 = generatePullRequest(
- prNumber: 1,
- repoName: cocoonRepo,
- );
- githubService.pullRequestData = pullRequest1;
- // 'member' is in the review nodes and 'author1' is the pr author.
- githubService.isTeamMemberMockMap['member'] = true;
- githubService.isTeamMemberMockMap['author1'] = true;
- final List<PullRequest> pullRequests = <PullRequest>[pullRequest1, pullRequest2];
- for (PullRequest pr in pullRequests) {
- unawaited(pubsub.publish(testTopic, pr));
- }
- githubService.checkRunsData = neutralCheckRunsMock;
- checkPullRequest = CheckPullRequest(
- config: config,
- pubsub: pubsub,
- cronAuthProvider: auth,
- );
- flutterRequest = PullRequestHelper(
- prNumber: 0,
- lastCommitHash: oid,
- );
- cocoonRequest = PullRequestHelper(
- prNumber: 1,
- lastCommitHash: oid,
- );
-
- await checkPullRequest.get();
- expectedOptions.add(flutterOption);
- expectedOptions.add(cocoonOption);
- verifyQueries(expectedOptions);
- assert(pubsub.messagesQueue.isEmpty);
- });
-
- test('Removes the label for the PR with failed tests', () async {
- final PullRequest pullRequest1 = generatePullRequest(prNumber: 0);
- final PullRequest pullRequest2 = generatePullRequest(
- prNumber: 1,
- repoName: cocoonRepo,
- );
- githubService.pullRequestData = pullRequest1;
- // 'member' is in the review nodes and 'author1' is the pr author.
- githubService.isTeamMemberMockMap['member'] = true;
- githubService.isTeamMemberMockMap['author1'] = true;
- final List<PullRequest> pullRequests = <PullRequest>[pullRequest1, pullRequest2];
- for (PullRequest pr in pullRequests) {
- unawaited(pubsub.publish(testTopic, pr));
- }
- githubService.checkRunsData = failedCheckRunsMock;
- checkPullRequest = CheckPullRequest(
- config: config,
- pubsub: pubsub,
- cronAuthProvider: auth,
- );
- flutterRequest = PullRequestHelper(
- prNumber: 0,
- lastCommitHash: oid,
- );
- cocoonRequest = PullRequestHelper(
- prNumber: 1,
- lastCommitHash: oid,
- );
-
- await checkPullRequest.get();
- expectedOptions.add(flutterOption);
- expectedOptions.add(cocoonOption);
- verifyQueries(expectedOptions);
- assert(pubsub.messagesQueue.isEmpty);
- });
-
- test('Removes the label for the PR with failed status', () async {
- final PullRequest pullRequest = generatePullRequest(prNumber: 0);
- githubService.pullRequestData = pullRequest;
- // 'member' is in the review nodes and 'author1' is the pr author.
- githubService.isTeamMemberMockMap['member'] = true;
- githubService.isTeamMemberMockMap['author1'] = true;
- unawaited(pubsub.publish(testTopic, pullRequest));
-
- checkPullRequest = CheckPullRequest(
- config: config,
- pubsub: pubsub,
- cronAuthProvider: auth,
- );
-
- flutterRequest = PullRequestHelper(
- lastCommitHash: oid,
- lastCommitStatuses: const <StatusHelper>[
- StatusHelper.flutterBuildSuccess,
- StatusHelper.otherStatusFailure,
- ],
- );
-
- await checkPullRequest.get();
- expectedOptions.add(flutterOption);
- verifyQueries(expectedOptions);
- assert(pubsub.messagesQueue.isEmpty);
- });
-
- test('Removes the label if non member does not have at least 2 member reviews', () async {
- final PullRequest pullRequest = generatePullRequest(
- prNumber: 0,
- );
- githubService.pullRequestData = pullRequest;
- // 'octocat' is the pr author from generatePullRequest calls.
- githubService.isTeamMemberMockMap['octocat'] = false;
- unawaited(pubsub.publish(testTopic, pullRequest));
-
- checkPullRequest = CheckPullRequest(
- config: config,
- pubsub: pubsub,
- cronAuthProvider: auth,
- );
-
- flutterRequest = PullRequestHelper(
- authorAssociation: '',
- lastCommitHash: oid,
- lastCommitStatuses: const <StatusHelper>[
- StatusHelper.flutterBuildSuccess,
- ],
- );
-
- await checkPullRequest.get();
- expectedOptions.add(flutterOption);
- verifyQueries(expectedOptions);
- assert(pubsub.messagesQueue.isEmpty);
- });
-
- test('Removes the label for the PR with null checks and statuses', () async {
- final PullRequest pullRequest = generatePullRequest(prNumber: 0);
- githubService.pullRequestData = pullRequest;
- // 'octocat' is the pr author from generatePullRequest calls.
- githubService.isTeamMemberMockMap['octocat'] = true;
- unawaited(pubsub.publish(testTopic, pullRequest));
-
- githubService.checkRunsData = emptyCheckRunsMock;
- checkPullRequest = CheckPullRequest(
- config: config,
- pubsub: pubsub,
- cronAuthProvider: auth,
- );
-
- flutterRequest = PullRequestHelper(
- lastCommitHash: oid,
- lastCommitStatuses: const <StatusHelper>[],
- );
-
- await checkPullRequest.get();
- expectedOptions.add(flutterOption);
- verifyQueries(expectedOptions);
- assert(pubsub.messagesQueue.isEmpty);
- });
-
- test('Does not merge PR with in progress checks', () async {
- final PullRequest pullRequest1 = generatePullRequest(prNumber: 0);
- final PullRequest pullRequest2 = generatePullRequest(
- prNumber: 1,
- repoName: cocoonRepo,
- );
- githubService.pullRequestData = pullRequest1;
- // 'member' is in the review nodes and 'author1' is the pr author.
- githubService.isTeamMemberMockMap['member'] = true;
- githubService.isTeamMemberMockMap['author1'] = true;
- final List<PullRequest> pullRequests = <PullRequest>[pullRequest1, pullRequest2];
- for (PullRequest pr in pullRequests) {
- unawaited(pubsub.publish(testTopic, pr));
- }
- githubService.checkRunsData = inProgressCheckRunsMock;
- checkPullRequest = CheckPullRequest(
- config: config,
- pubsub: pubsub,
- cronAuthProvider: auth,
- );
- flutterRequest = PullRequestHelper(prNumber: 0);
- cocoonRequest = PullRequestHelper(prNumber: 1);
- await checkPullRequest.get();
- expectedOptions.add(flutterOption);
- expectedOptions.add(cocoonOption);
- verifyQueries(expectedOptions);
- expect(pubsub.messagesQueue.length, 2);
- pubsub.messagesQueue.clear();
- });
-
- test('Does not merge PR if no autosubmit label any more', () async {
- final PullRequest pullRequest1 = generatePullRequest(
- prNumber: 0,
- autosubmitLabel: noAutosubmitLabel,
- );
- final PullRequest pullRequest2 = generatePullRequest(
- prNumber: 1,
- autosubmitLabel: noAutosubmitLabel,
- repoName: cocoonRepo,
- );
- githubService.pullRequestData = pullRequest1;
- final List<PullRequest> pullRequests = <PullRequest>[pullRequest1, pullRequest2];
- for (PullRequest pr in pullRequests) {
- unawaited(pubsub.publish(testTopic, pr));
- }
- checkPullRequest = CheckPullRequest(
- config: config,
- pubsub: pubsub,
- cronAuthProvider: auth,
- );
- flutterRequest = PullRequestHelper(prNumber: 0);
- cocoonRequest = PullRequestHelper(prNumber: 1);
- await checkPullRequest.get();
- assert(pubsub.messagesQueue.isEmpty);
- });
-
- test('Self review is disallowed', () async {
- final PullRequest pullRequest = generatePullRequest(
- prNumber: 0,
- author: 'some_rando',
- );
- githubService.pullRequestData = pullRequest;
- // 'octocat' is the pr author from generatePullRequest calls.
- githubService.isTeamMemberMockMap['some_rando'] = true;
- unawaited(pubsub.publish(testTopic, pullRequest));
- checkPullRequest = CheckPullRequest(
- config: config,
- pubsub: pubsub,
- cronAuthProvider: auth,
- );
- flutterRequest = PullRequestHelper(
- author: 'some_rando',
- lastCommitHash: oid,
- authorAssociation: 'MEMBER',
- reviews: <PullRequestReviewHelper>[
- const PullRequestReviewHelper(
- authorName: 'some_rando',
- state: ReviewState.APPROVED,
- memberType: MemberType.MEMBER,
- ),
- ],
- lastCommitStatuses: const <StatusHelper>[
- StatusHelper.flutterBuildSuccess,
- ],
- );
- await checkPullRequest.get();
- expectedOptions.add(flutterOption);
- verifyQueries(expectedOptions);
- // Re-add to queue to poll for reviews
- assert(pubsub.messagesQueue.isNotEmpty);
- });
-
- test('All messages are pulled', () async {
- for (int i = 0; i < 3; i++) {
- final PullRequest pullRequest = generatePullRequest(
- prNumber: i,
- repoName: cocoonRepo,
- );
- unawaited(
- pubsub.publish(
- 'auto-submit-queue-sub',
- pullRequest,
- ),
- );
- }
-
- checkPullRequest = CheckPullRequest(
- config: config,
- pubsub: pubsub,
- cronAuthProvider: auth,
- );
- cocoonRequest = PullRequestHelper(
- prNumber: 0,
- lastCommitHash: oid,
- );
- final List<pub.ReceivedMessage> messages = await checkPullRequest.pullMessages(testSubscription, 5, 5);
- expect(messages.length, 3);
- });
- });
-}
-
-QueryResult createQueryResult(PullRequestHelper pullRequest) {
- return createFakeQueryResult(
- data: <String, dynamic>{
- 'repository': <String, dynamic>{
- 'pullRequest': pullRequest.toEntry().cast<String, dynamic>(),
- },
- },
- );
-}
-
-Map<String, dynamic> getMergePullRequestVariables(
- String id,
- String number,
-) {
- return <String, dynamic>{
- 'id': id,
- 'oid': oid,
- 'title': '$title (#$number)',
- };
-}
diff --git a/auto_submit/test/requests/github_webhook_test.dart b/auto_submit/test/requests/github_webhook_test.dart
deleted file mode 100644
index 96b262b..0000000
--- a/auto_submit/test/requests/github_webhook_test.dart
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2022 The Flutter 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:convert';
-import 'dart:typed_data';
-
-import 'package:auto_submit/requests/github_webhook.dart';
-import 'package:auto_submit/requests/exceptions.dart';
-import 'package:crypto/crypto.dart';
-import 'package:github/github.dart';
-import 'package:shelf/shelf.dart';
-import 'package:test/test.dart';
-
-import './github_webhook_test_data.dart';
-import '../src/request_handling/fake_pubsub.dart';
-import '../src/service/fake_config.dart';
-
-void main() {
- group('Check Webhook', () {
- late Request req;
- late GithubWebhook githubWebhook;
- const String keyString = 'not_a_real_key';
- final FakeConfig config = FakeConfig(webhookKey: keyString);
- final FakePubSub pubsub = FakePubSub();
- late Map<String, String> validHeader;
- late Map<String, String> inValidHeader;
-
- String getHmac(Uint8List list, Uint8List key) {
- final Hmac hmac = Hmac(sha1, key);
- return hmac.convert(list).toString();
- }
-
- setUp(() {
- githubWebhook = GithubWebhook(config: config, pubsub: pubsub);
- });
-
- test('call handler to handle the post request', () async {
- final Uint8List body = utf8.encode(generateWebhookEvent());
- final Uint8List key = utf8.encode(keyString);
- final String hmac = getHmac(body, key);
- validHeader = <String, String>{'X-Hub-Signature': 'sha1=$hmac', 'X-GitHub-Event': 'yes'};
- req = Request('POST', Uri.parse('http://localhost/'), body: generateWebhookEvent(), headers: validHeader);
- final Response response = await githubWebhook.post(req);
- final String resBody = await response.readAsString();
- final reqBody = json.decode(resBody) as Map<String, dynamic>;
- final List<IssueLabel> labels = PullRequest.fromJson(reqBody['pull_request'] as Map<String, dynamic>).labels!;
- expect(labels[0].name, 'cla: yes');
- expect(labels[1].name, 'autosubmit');
- });
-
- test('Rejects invalid hmac', () async {
- inValidHeader = <String, String>{'X-GitHub-Event': 'pull_request', 'X-Hub-Signature': 'bar'};
- req = Request('POST', Uri.parse('http://localhost/'), body: 'Hello, World!', headers: inValidHeader);
- await expectLater(githubWebhook.post(req), throwsA(isA<Forbidden>()));
- });
-
- test('Rejects missing headers', () async {
- req = Request('POST', Uri.parse('http://localhost/'), body: generateWebhookEvent());
- await expectLater(githubWebhook.post(req), throwsA(isA<BadRequestException>()));
- });
- });
-}
diff --git a/auto_submit/test/requests/github_webhook_test_data.dart b/auto_submit/test/requests/github_webhook_test_data.dart
deleted file mode 100644
index ace27b7..0000000
--- a/auto_submit/test/requests/github_webhook_test_data.dart
+++ /dev/null
@@ -1,562 +0,0 @@
-// Copyright 2022 The Flutter 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:convert';
-
-import 'package:auto_submit/service/config.dart';
-import 'package:github/github.dart';
-
-String generateWebhookEvent({
- String? labelName,
- String? autosubmitLabel,
- String? repoName,
- String? login,
- String? authorAssociation,
-}) {
- return '''{
- "action": "open",
- "number": 1598,
- "sender": {
- "login": "matanlurey",
- "id": 168174,
- "node_id": "MDQ6VXNlcjE2ODE3NA=="
- },
- "pull_request": {
- "id": 1,
- "number": 1347,
- "state": "open",
- "title": "Amazing new feature",
- "user": {
- "login": "${login ?? "octocat"}",
- "id": 1
- },
- "body": "Please pull these awesome changes in!",
- "labels": [
- {
- "id": 487496476,
- "name": "${labelName ?? "cla: yes"}"
- },
- {
- "id": 284437560,
- "name": "${autosubmitLabel ?? "autosubmit"}"
- }
- ],
- "created_at": "2011-01-26T19:01:12Z",
- "head": {
- "label": "octocat:new-topic",
- "sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e",
- "repo": {
- "id": 1296269,
- "name": "Hello-World",
- "full_name": "octocat/Hello-World",
- "owner": {
- "login": "octocat",
- "id": 1,
- "avatar_url": "https://github.com/images/error/octocat_happy.gif",
- "html_url": "https://github.com/octocat"
- }
- }
- },
- "base": {
- "label": "octocat:master",
- "sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e",
- "repo": {
- "id": 1296269,
- "name": "${repoName ?? "flutter"}",
- "full_name": "${login ?? "flutter"}/${repoName ?? "flutter"}",
- "owner": {
- "login": "${login ?? "flutter"}",
- "id": 1,
- "avatar_url": "https://github.com/images/error/octocat_happy.gif",
- "html_url": "https://github.com/octocat"
- }
- }
- },
- "author_association": "${authorAssociation ?? "OWNER"}",
- "mergeable": true,
- "mergeable_state": "clean"
- }
- }''';
-}
-
-PullRequest generatePullRequest({
- String labelName = 'cla: yes',
- String? autosubmitLabel = Config.kAutosubmitLabel,
- String repoName = 'flutter',
- String login = 'flutter',
- String authorAssociation = 'OWNER',
- String author = 'octocat',
- int prNumber = 1347,
- String state = 'open',
- String? body = 'Please pull these awesome changes in!',
- String title = 'Amazing new feature',
- bool? mergeable = true,
- String baseRef = 'main',
- DateTime? mergedAt,
-}) {
- return PullRequest.fromJson(
- json.decode('''{
- "id": 1,
- "number": $prNumber,
- "state": "$state",
- "title": ${jsonEncode(title)},
- "user": {
- "login": "$author",
- "id": 1
- },
- "body": "$body",
- "labels": [
- {
- "id": 487496476,
- "name": "$labelName"
- },
- {
- "id": 284437560,
- "name": "$autosubmitLabel"
- }
- ],
- "created_at": "2011-01-26T19:01:12Z",
- "merged_at": "${mergedAt ?? DateTime.now().subtract(const Duration(hours: 12))}",
- "closed_at": "2011-01-26T19:10:12Z",
- "head": {
- "label": "octocat:new-topic",
- "ref": "new-topic",
- "sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e",
- "repo": {
- "id": 1296269,
- "name": "Hello-World",
- "full_name": "octocat/Hello-World",
- "owner": {
- "login": "octocat",
- "id": 1,
- "avatar_url": "https://github.com/images/error/octocat_happy.gif",
- "html_url": "https://github.com/octocat"
- }
- }
- },
- "base": {
- "label": "octocat:master",
- "label": "octocat:main",
- "ref": "$baseRef",
- "sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e",
- "repo": {
- "id": 1296269,
- "name": "$repoName",
- "full_name": "$login/$repoName",
- "owner": {
- "login": "$login",
- "id": 1,
- "avatar_url": "https://github.com/images/error/octocat_happy.gif",
- "html_url": "https://github.com/octocat"
- }
- }
- },
- "author_association": "$authorAssociation",
- "mergeable": $mergeable,
- "mergeable_state": "clean"
- }''') as Map<String, dynamic>,
- );
-}
-
-const String reviewsMock = '''[
- {
- "id": 80,
- "user": {
- "login": "octocat2",
- "id": 1
- },
- "body": "Here is the body for the review.",
- "state": "APPROVED",
- "author_association": "OWNER"
- }
-]''';
-
-const String unApprovedReviewsMock = '''[
- {
- "id": 81,
- "user": {
- "login": "octocat",
- "id": 1
- },
- "body": "Here is the body for the review.",
- "state": "CHANGES_REQUESTED",
- "author_association": "OWNER"
- }
-]''';
-
-const String checkRunsMock = '''{
- "total_count": 1,
- "check_runs": [
- {
- "id": 1,
- "head_sha": "be6ff099a4ee56e152a5fa2f37edd10f79d1269a",
- "external_id": "",
- "details_url": "https://example.com",
- "status": "completed",
- "conclusion": "success",
- "started_at": "2018-05-04T01:14:52Z",
- "name": "mighty_readme",
- "check_suite": {
- "id": 5
- }
- }
- ]
-}''';
-
-const String failedCheckRunsMock = '''{
- "total_count": 1,
- "check_runs": [
- {
- "id": 2,
- "head_sha": "be6ff099a4ee56e152a5fa2f37edd10f79d1269a",
- "external_id": "",
- "details_url": "https://example.com",
- "status": "completed",
- "conclusion": "failure",
- "started_at": "2018-05-04T01:14:52Z",
- "name": "failed_checkrun",
- "check_suite": {
- "id": 5
- }
- }
- ]
-}''';
-
-const String neutralCheckRunsMock = '''{
- "total_count": 1,
- "check_runs": [
- {
- "id": 2,
- "head_sha": "be6ff099a4ee56e152a5fa2f37edd10f79d1269a",
- "external_id": "",
- "details_url": "https://example.com",
- "status": "completed",
- "conclusion": "neutral",
- "started_at": "2018-05-04T01:14:52Z",
- "name": "neutral_checkrun",
- "check_suite": {
- "id": 5
- }
- }
- ]
-}''';
-
-const String inProgressCheckRunsMock = '''{
- "total_count": 1,
- "check_runs": [
- {
- "id": 3,
- "head_sha": "be6ff099a4ee56e152a5fa2f37edd10f79d1269a",
- "external_id": "",
- "details_url": "https://example.com",
- "status": "in_progress",
- "conclusion": "neutral",
- "started_at": "2018-05-04T01:14:52Z",
- "name": "inprogress_checkrun",
- "check_suite": {
- "id": 5
- }
- }
- ]
-}''';
-
-const String skippedCheckRunsMock = '''{
- "total_count": 1,
- "check_runs": [
- {
- "id": 6,
- "head_sha": "be6ff099a4ee56e152a5fa2f37edd10f79d1269a",
- "external_id": "",
- "details_url": "https://example.com",
- "status": "in_progress",
- "conclusion": "skipped",
- "started_at": "2018-05-04T01:14:52Z",
- "name": "inprogress_checkrun",
- "check_suite": {
- "id": 5
- }
- }
- ]
-}''';
-
-const String multipleCheckRunsMock = '''{
- "total_count": 3,
- "check_runs": [
- {
- "id": 1,
- "head_sha": "be6ff099a4ee56e152a5fa2f37edd10f79d1269a",
- "external_id": "",
- "details_url": "https://example.com",
- "status": "completed",
- "conclusion": "success",
- "started_at": "2018-05-04T01:14:52Z",
- "name": "mighty_readme",
- "check_suite": {
- "id": 5
- }
- },
- {
- "id": 2,
- "head_sha": "be6ff099a4ee56e152a5fa2f37edd10f79d1269a",
- "external_id": "",
- "details_url": "https://example.com",
- "status": "completed",
- "conclusion": "neutral",
- "started_at": "2018-05-04T01:14:52Z",
- "name": "neutral_checkrun",
- "check_suite": {
- "id": 5
- }
- },
- {
- "id": 6,
- "head_sha": "be6ff099a4ee56e152a5fa2f37edd10f79d1269a",
- "external_id": "",
- "details_url": "https://example.com",
- "status": "in_progress",
- "conclusion": "skipped",
- "started_at": "2018-05-04T01:14:52Z",
- "name": "inprogress_checkrun",
- "check_suite": {
- "id": 5
- }
- }
- ]
-}''';
-
-const String multipleCheckRunsWithFailureMock = '''{
- "total_count": 3,
- "check_runs": [
- {
- "id": 1,
- "head_sha": "be6ff099a4ee56e152a5fa2f37edd10f79d1269a",
- "external_id": "",
- "details_url": "https://example.com",
- "status": "completed",
- "conclusion": "success",
- "started_at": "2018-05-04T01:14:52Z",
- "name": "mighty_readme",
- "check_suite": {
- "id": 5
- }
- },
- {
- "id": 2,
- "head_sha": "be6ff099a4ee56e152a5fa2f37edd10f79d1269a",
- "external_id": "",
- "details_url": "https://example.com",
- "status": "completed",
- "conclusion": "failure",
- "started_at": "2018-05-04T01:14:52Z",
- "name": "failed_checkrun",
- "check_suite": {
- "id": 5
- }
- },
- {
- "id": 6,
- "head_sha": "be6ff099a4ee56e152a5fa2f37edd10f79d1269a",
- "external_id": "",
- "details_url": "https://example.com",
- "status": "in_progress",
- "conclusion": "skipped",
- "started_at": "2018-05-04T01:14:52Z",
- "name": "inprogress_checkrun",
- "check_suite": {
- "id": 5
- }
- }
- ]
-}''';
-
-const String inprogressAndNotFailedCheckRunMock = '''{
- "total_count": 1,
- "check_runs": [
- {
- "id": 6,
- "head_sha": "be6ff099a4ee56e152a5fa2f37edd10f79d1269a",
- "external_id": "",
- "details_url": "https://example.com",
- "status": "in_progress",
- "conclusion": "neutral",
- "started_at": "2018-05-04T01:14:52Z",
- "name": "inprogress_checkrun",
- "check_suite": {
- "id": 5
- }
- }
- ]
-}''';
-
-const String emptyCheckRunsMock = '''{"check_runs": [{}]}''';
-
-// repositoryStatusesMock is from the official Github API: https://developer.github.com/v3/repos/statuses/#list-statuses-for-a-specific-ref
-// state can be error, failure, pending, success
-const String repositoryStatusesMock = '''{
- "state": "success",
- "statuses": [
- {
- "state": "success",
- "context": "tree-status"
- },
- {
- "state": "success",
- "context": "tree-status/flutter"
- }
- ]
-}''';
-
-const String repositoryStatusesWithGoldMock = '''{
- "state": "success",
- "statuses": [
- {
- "state": "success",
- "context": "tree-status"
- },
- {
- "state": "PENDING",
- "context": "flutter-gold"
- }
- ]
-}''';
-
-const String repositoryStatusesWithFailedGoldMock = '''{
- "state": "success",
- "statuses": [
- {
- "state": "success",
- "context": "tree-status"
- },
- {
- "state": "failure",
- "context": "flutter-gold",
- "targetUrl": "https://ci.example.com/1000/output"
- }
- ]
-}''';
-
-const String repositoryStatusesNonLuciFlutterMock = '''{
- "state": "success",
- "statuses": [
- {
- "state": "success",
- "context": "infra"
- },
- {
- "state": "success",
- "context": "config"
- }
- ]
-}''';
-
-const String failedAuthorsStatusesMock = '''{
- "state": "failure",
- "statuses": [
- {
- "state": "failure",
- "context": "tree-status",
- "targetUrl": "https://ci.example.com/1000/output"
- },
- {
- "state": "failure",
- "context": "tree-status",
- "targetUrl": "https://ci.example.com/2000/output"
- }
- ]
-}''';
-
-const String failedNonAuthorsStatusesMock = '''{
- "state": "failure",
- "statuses": [
- {
- "state": "failure",
- "context": "flutter-engine",
- "targetUrl": "https://ci.example.com/1000/output"
- },
- {
- "state": "failure",
- "context": "flutter-infra",
- "targetUrl": "https://ci.example.com/2000/output"
- }
- ]
-}''';
-
-const String emptyStatusesMock = '''{"statuses": [{}]}''';
-
-// commitMock is from the official Github API: https://docs.github.com/en/rest/reference/commits#get-a-commit
-const String commitMock = '''{
- "sha": "HEAD~",
- "commit": {
- "url": "https://api.github.com/repos/octocat/Hello-World/git/commits/6dcb09b5b57875f334f61aebed695e2e4193db5e",
- "message": "Fix all the bugs"
- }
-}''';
-
-const String shouldRebaseMock = '''{
- "url": "https://api.github.com/repos/octocat/Hello-World/compare/master...topic",
- "status": "behind",
- "ahead_by": 1,
- "behind_by": 11,
- "total_commits": 1,
- "files": [
- {
- "sha": "bbcd538c8e72b8c175046e27cc8f907076331401",
- "filename": "file1.txt",
- "status": "added",
- "changes": 124
- }
- ]
-}''';
-
-// compareTwoCommitsMock is from the official Github API: https://docs.github.com/en/rest/reference/commits#compare-two-commits
-const String compareTwoCommitsMock = '''{
- "url": "https://api.github.com/repos/octocat/Hello-World/compare/master...topic",
- "status": "behind",
- "ahead_by": 1,
- "behind_by": 2,
- "total_commits": 1,
- "files": [
- {
- "sha": "bbcd538c8e72b8c175046e27cc8f907076331401",
- "filename": "file1.txt",
- "status": "added",
- "changes": 124
- }
- ]
-}''';
-
-const String compareToTCommitsMock = '''{
- "url": "https://api.github.com/repos/octocat/Hello-World/compare/master...topic",
- "status": "behind",
- "ahead_by": 1,
- "behind_by": 2,
- "total_commits": 1,
- "files": []
-}''';
-
-// successMergeMock is from the offcial github API: https://docs.github.com/en/rest/reference/pulls#merge-a-pull-request.
-const String successMergeMock = '''
-{
- "sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e",
- "merged": true
-}''';
-
-// createCommentMock is from the offcial github API: https://docs.github.com/en/rest/reference/pulls#create-a-review-comment-for-a-pull-request.
-const String createCommentMock = '''
-{
- "id": 10,
- "position": 1,
- "commit_id": "6dcb09b5b57875f334f61aebed695e2e4193db5e",
- "user": {
- "login": "octocat",
- "id": 1
- },
- "body": "Great stuff!"
-}''';
-
-const String pullRequestMergeMock = '''
-{
- "sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e",
- "merged": true
-}''';
diff --git a/auto_submit/test/service/approver_service_test.dart b/auto_submit/test/service/approver_service_test.dart
deleted file mode 100644
index 003f743..0000000
--- a/auto_submit/test/service/approver_service_test.dart
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2022 The Flutter 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 'package:auto_submit/configuration/repository_configuration.dart';
-import 'package:auto_submit/service/approver_service.dart';
-import 'package:github/github.dart' as gh;
-import 'package:mockito/mockito.dart';
-import 'package:test/test.dart';
-
-import '../configuration/repository_configuration_data.dart';
-import '../requests/github_webhook_test_data.dart';
-import '../src/service/fake_config.dart';
-import '../utilities/mocks.dart';
-
-void main() {
- FakeConfig config;
- late ApproverService service;
- late MockGitHub github;
- late MockPullRequestsService pullRequests;
-
- setUp(() {
- github = MockGitHub();
- config = FakeConfig(githubClient: github);
- config.repositoryConfigurationMock = RepositoryConfiguration.fromYaml(sampleConfigNoOverride);
- service = ApproverService(config);
- pullRequests = MockPullRequestsService();
- when(github.pullRequests).thenReturn(pullRequests);
- when(pullRequests.createReview(any, any)).thenAnswer((_) async => gh.PullRequestReview(id: 123, user: gh.User()));
- });
-
- test('Verify approval ignored', () async {
- final gh.PullRequest pr = generatePullRequest(author: 'not_a_user');
- await service.autoApproval(pr);
- verifyNever(pullRequests.createReview(any, captureAny));
- });
-
- test('Verify approve', () async {
- when(pullRequests.listReviews(any, any)).thenAnswer((_) => const Stream<gh.PullRequestReview>.empty());
- final gh.PullRequest pr = generatePullRequest(author: 'dependabot[bot]');
- await service.autoApproval(pr);
- final List<dynamic> reviews = verify(pullRequests.createReview(any, captureAny)).captured;
- expect(reviews.length, 1);
- final gh.CreatePullRequestReview review = reviews.single as gh.CreatePullRequestReview;
- expect(review.event, 'APPROVE');
- });
-
- test('Already approved', () async {
- final gh.PullRequestReview review =
- gh.PullRequestReview(id: 123, user: gh.User(login: 'fluttergithubbot'), state: 'APPROVED');
- when(pullRequests.listReviews(any, any)).thenAnswer((_) => Stream<gh.PullRequestReview>.value(review));
- final gh.PullRequest pr = generatePullRequest(author: 'dependabot[bot]');
- await service.autoApproval(pr);
- verifyNever(pullRequests.createReview(any, captureAny));
- });
-}
diff --git a/auto_submit/test/service/bigquery_test.dart b/auto_submit/test/service/bigquery_test.dart
deleted file mode 100644
index ae67c4c..0000000
--- a/auto_submit/test/service/bigquery_test.dart
+++ /dev/null
@@ -1,591 +0,0 @@
-// Copyright 2022 The Flutter 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:convert';
-
-import 'package:auto_submit/exception/bigquery_exception.dart';
-import 'package:auto_submit/model/big_query_pull_request_record.dart';
-import 'package:googleapis/bigquery/v2.dart';
-import 'package:mockito/mockito.dart';
-import 'package:test/expect.dart';
-import 'package:test/scaffolding.dart';
-
-import '../src/service/fake_bigquery_service.dart';
-import '../utilities/mocks.dart';
-
-const String revertRequestRecordResponse = '''
-{
- "jobComplete": true,
- "rows": [
- { "f": [
- { "v": "flutter"},
- { "v": "cocoon" },
- { "v": "ricardoamador" },
- { "v": "1024" },
- { "v": "123f124" },
- { "v": "123456789" },
- { "v": "123456999" },
- { "v": "ricardoamador" },
- { "v": "2048" },
- { "v": "ce345dc" },
- { "v": "234567890" },
- { "v": "234567999" },
- { "v": "ricardoamador" },
- { "v": "11304" },
- { "v": "1640979000000" },
- { "v": "0" },
- { "v": "" }
- ]
- }
- ]
-}
-''';
-
-const String pullRequestRecordResponse = '''
-{
- "jobComplete": true,
- "rows": [
- { "f": [
- { "v": "123456789"},
- { "v": "234567890" },
- { "v": "flutter" },
- { "v": "cocoon" },
- { "v": "ricardoamador" },
- { "v": "345" },
- { "v": "ade456" },
- { "v": "merge" }
- ]
- }
- ]
-}
-''';
-
-const String successResponseNoRowsAffected = '''
-{
- "jobComplete": true
-}
-''';
-
-const String insertDeleteUpdateSuccessResponse = '''
-{
- "jobComplete": true,
- "numDmlAffectedRows": "1"
-}
-''';
-
-const String insertDeleteUpdateSuccessTooManyRows = '''
-{
- "jobComplete": true,
- "numDmlAffectedRows": "2"
-}
-''';
-
-const String selectPullRequestTooManyRowsResponse = '''
-{
- "jobComplete": true,
- "numDmlAffectedRows": "2",
- "rows": [
- { "f": [
- { "v": "123456789"},
- { "v": "234567890" },
- { "v": "flutter" },
- { "v": "cocoon" },
- { "v": "ricardoamador" },
- { "v": "345" },
- { "v": "ade456" },
- { "v": "merge" }
- ]
- },
- { "f": [
- { "v": "123456789"},
- { "v": "234567890" },
- { "v": "flutter" },
- { "v": "cocoon" },
- { "v": "ricardoamador" },
- { "v": "345" },
- { "v": "ade456" },
- { "v": "merge" }
- ]
- }
- ]
-}
-''';
-
-const String selectRevertRequestTooManyRowsResponse = '''
-{
- "jobComplete": true,
- "numDmlAffectedRows": "2",
- "rows": [
- { "f": [
- { "v": "flutter"},
- { "v": "cocoon" },
- { "v": "ricardoamador" },
- { "v": "1024" },
- { "v": "123f124" },
- { "v": "123456789" },
- { "v": "123456999" },
- { "v": "ricardoamador" },
- { "v": "2048" },
- { "v": "ce345dc" },
- { "v": "234567890" },
- { "v": "234567999" }
- ]
- },
- { "f": [
- { "v": "flutter"},
- { "v": "cocoon" },
- { "v": "ricardoamador" },
- { "v": "1024" },
- { "v": "123f124" },
- { "v": "123456789" },
- { "v": "123456999" },
- { "v": "ricardoamador" },
- { "v": "2048" },
- { "v": "ce345dc" },
- { "v": "234567890" },
- { "v": "234567999" }
- ]
- }
- ]
-}
-''';
-
-const String errorResponse = '''
-{
- "jobComplete": false
-}
-''';
-
-const String selectReviewRequestRecordsResponse = '''
-{
- "jobComplete": true,
- "numDmlAffectedRows": "2",
- "rows": [
- { "f": [
- { "v": "Keyonghan" },
- { "v": "2048" },
- { "v": "234567890" },
- { "v": "0" },
- { "v": "" }
- ]
- },
- { "f": [
- { "v": "caseyhillers" },
- { "v": "2049" },
- { "v": "234567890" },
- { "v": "0" },
- { "v": "" }
- ]
- }
- ]
-}
-''';
-
-const String expectedProjectId = 'flutter-dashboard';
-
-void main() {
- late FakeBigqueryService service;
- late MockJobsResource jobsResource;
-
- setUp(() {
- jobsResource = MockJobsResource();
- service = FakeBigqueryService(jobsResource);
- });
-
- test('Insert pull request record is successful.', () async {
- when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) {
- return Future<QueryResponse>.value(
- QueryResponse.fromJson(jsonDecode(insertDeleteUpdateSuccessResponse) as Map<dynamic, dynamic>),
- );
- });
-
- final PullRequestRecord pullRequestRecord = PullRequestRecord(
- prCreatedTimestamp: DateTime.fromMillisecondsSinceEpoch(123456789),
- prLandedTimestamp: DateTime.fromMillisecondsSinceEpoch(234567890),
- organization: 'flutter',
- repository: 'cocoon',
- author: 'ricardoamador',
- prNumber: 345,
- prCommit: 'ade456',
- prRequestType: 'merge',
- );
-
- bool hasError = false;
- try {
- await service.insertPullRequestRecord(
- projectId: expectedProjectId,
- pullRequestRecord: pullRequestRecord,
- );
- } on BigQueryException {
- hasError = true;
- }
- expect(hasError, isFalse);
- });
-
- test('Insert pull request record handles unsuccessful job complete error.', () async {
- when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) {
- return Future<QueryResponse>.value(
- QueryResponse.fromJson(jsonDecode(errorResponse) as Map<dynamic, dynamic>),
- );
- });
-
- bool hasError = false;
- final PullRequestRecord pullRequestRecord = PullRequestRecord(
- prCreatedTimestamp: DateTime.fromMillisecondsSinceEpoch(123456789),
- prLandedTimestamp: DateTime.fromMillisecondsSinceEpoch(234567890),
- organization: 'flutter',
- repository: 'cocoon',
- author: 'ricardoamador',
- prNumber: 345,
- prCommit: 'ade456',
- prRequestType: 'merge',
- );
-
- try {
- await service.insertPullRequestRecord(
- projectId: expectedProjectId,
- pullRequestRecord: pullRequestRecord,
- );
- } on BigQueryException catch (exception) {
- expect(exception.cause, 'Insert pull request $pullRequestRecord did not complete.');
- hasError = true;
- }
- expect(hasError, isTrue);
- });
-
- test('Insert pull request fails when multiple rows are returned.', () async {
- when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) {
- return Future<QueryResponse>.value(
- QueryResponse.fromJson(jsonDecode(selectPullRequestTooManyRowsResponse) as Map<dynamic, dynamic>),
- );
- });
-
- bool hasError = false;
- final PullRequestRecord pullRequestRecord = PullRequestRecord(
- prCreatedTimestamp: DateTime.fromMillisecondsSinceEpoch(123456789),
- prLandedTimestamp: DateTime.fromMillisecondsSinceEpoch(234567890),
- organization: 'flutter',
- repository: 'cocoon',
- author: 'ricardoamador',
- prNumber: 345,
- prCommit: 'ade456',
- prRequestType: 'merge',
- );
-
- try {
- await service.insertPullRequestRecord(
- projectId: expectedProjectId,
- pullRequestRecord: pullRequestRecord,
- );
- } on BigQueryException catch (exception) {
- expect(exception.cause, 'There was an error inserting $pullRequestRecord into the table.');
- hasError = true;
- }
- expect(hasError, isTrue);
- });
-
- test('Select pull request is successful.', () async {
- when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) {
- return Future<QueryResponse>.value(
- QueryResponse.fromJson(jsonDecode(pullRequestRecordResponse) as Map<dynamic, dynamic>),
- );
- });
-
- final PullRequestRecord pullRequestRecord = await service.selectPullRequestRecordByPrNumber(
- projectId: expectedProjectId,
- prNumber: 345,
- repository: 'cocoon',
- );
-
- expect(pullRequestRecord, isNotNull);
- expect(pullRequestRecord.prCreatedTimestamp, equals(DateTime.fromMillisecondsSinceEpoch(123456789)));
- expect(pullRequestRecord.prLandedTimestamp, equals(DateTime.fromMillisecondsSinceEpoch(234567890)));
- expect(pullRequestRecord.organization, equals('flutter'));
- expect(pullRequestRecord.repository, equals('cocoon'));
- expect(pullRequestRecord.author, equals('ricardoamador'));
- expect(pullRequestRecord.prNumber, 345);
- expect(pullRequestRecord.prCommit, equals('ade456'));
- expect(pullRequestRecord.prRequestType, equals('merge'));
- });
-
- test('Select pull request handles unsuccessful job failure.', () async {
- when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) {
- return Future<QueryResponse>.value(QueryResponse.fromJson(jsonDecode(errorResponse) as Map<dynamic, dynamic>));
- });
-
- bool hasError = false;
- try {
- await service.selectPullRequestRecordByPrNumber(
- projectId: expectedProjectId,
- prNumber: 345,
- repository: 'cocoon',
- );
- } on BigQueryException catch (exception) {
- hasError = true;
- expect(exception.cause, 'Get pull request by pr# 345 in repository cocoon did not complete.');
- }
- expect(hasError, isTrue);
- });
-
- test('Select pull request handles no rows returned failure.', () async {
- when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) {
- return Future<QueryResponse>.value(
- QueryResponse.fromJson(jsonDecode(successResponseNoRowsAffected) as Map<dynamic, dynamic>),
- );
- });
-
- bool hasError = false;
- try {
- await service.selectPullRequestRecordByPrNumber(
- projectId: expectedProjectId,
- prNumber: 345,
- repository: 'cocoon',
- );
- } on BigQueryException catch (exception) {
- hasError = true;
- expect(
- exception.cause,
- 'Could not find an entry for pull request with pr# 345 in repository cocoon.',
- );
- }
- expect(hasError, isTrue);
- });
-
- test('Select pull request handles too many rows returned failure.', () async {
- when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) {
- return Future<QueryResponse>.value(
- QueryResponse.fromJson(jsonDecode(selectPullRequestTooManyRowsResponse) as Map<dynamic, dynamic>),
- );
- });
-
- bool hasError = false;
- try {
- await service.selectPullRequestRecordByPrNumber(
- projectId: expectedProjectId,
- prNumber: 345,
- repository: 'cocoon',
- );
- } on BigQueryException catch (exception) {
- hasError = true;
- expect(
- exception.cause,
- 'More than one record was returned for pull request with pr# 345 in repository cocoon.',
- );
- }
- expect(hasError, isTrue);
- });
-
- test('Delete pull request record handles failure to complete job.', () async {
- when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) {
- return Future<QueryResponse>.value(QueryResponse.fromJson(jsonDecode(errorResponse) as Map<dynamic, dynamic>));
- });
-
- bool hasError = false;
- try {
- await service.deletePullRequestRecord(
- projectId: expectedProjectId,
- prNumber: 345,
- repository: 'cocoon',
- );
- } on BigQueryException catch (exception) {
- hasError = true;
- expect(
- exception.cause,
- 'Delete pull request with pr# 345 in repository cocoon did not complete.',
- );
- }
- expect(hasError, isTrue);
- });
-
- test('Delete pull request record handles success but no affected rows.', () async {
- when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) {
- return Future<QueryResponse>.value(
- QueryResponse.fromJson(jsonDecode(successResponseNoRowsAffected) as Map<dynamic, dynamic>),
- );
- });
-
- bool hasError = false;
- try {
- await service.deletePullRequestRecord(
- projectId: expectedProjectId,
- prNumber: 345,
- repository: 'cocoon',
- );
- } on BigQueryException catch (exception) {
- hasError = true;
- expect(
- exception.cause,
- 'Could not find pull request with pr# 345 in repository cocoon to delete.',
- );
- }
- expect(hasError, isTrue);
- });
-
- test('Delete pull request record handles success but wrong number of affected rows.', () async {
- when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) {
- return Future<QueryResponse>.value(
- QueryResponse.fromJson(jsonDecode(insertDeleteUpdateSuccessTooManyRows) as Map<dynamic, dynamic>),
- );
- });
-
- bool hasError = false;
- try {
- await service.deletePullRequestRecord(
- projectId: expectedProjectId,
- prNumber: 345,
- repository: 'cocoon',
- );
- } on BigQueryException catch (exception) {
- hasError = true;
- expect(
- exception.cause,
- 'More than one row was deleted from the database for pull request with pr# 345 in repository cocoon.',
- );
- }
- expect(hasError, isTrue);
- });
-
- test('Delete revert request record handles failure to complete job.', () async {
- when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) {
- return Future<QueryResponse>.value(
- QueryResponse.fromJson(jsonDecode(errorResponse) as Map<dynamic, dynamic>),
- );
- });
-
- bool hasError = false;
- try {
- await service.deleteRevertRequestRecord(
- projectId: expectedProjectId,
- prNumber: 2048,
- repository: 'cocoon',
- );
- } on BigQueryException catch (exception) {
- hasError = true;
- expect(
- exception.cause,
- 'Delete revert request with pr# 2048 in repository cocoon did not complete.',
- );
- }
- expect(hasError, isTrue);
- });
-
- test('Delete revert request record handles success but no affected rows.', () async {
- when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) {
- return Future<QueryResponse>.value(
- QueryResponse.fromJson(jsonDecode(successResponseNoRowsAffected) as Map<dynamic, dynamic>),
- );
- });
-
- bool hasError = false;
- try {
- await service.deleteRevertRequestRecord(
- projectId: expectedProjectId,
- prNumber: 2048,
- repository: 'cocoon',
- );
- } on BigQueryException catch (exception) {
- hasError = true;
- expect(
- exception.cause,
- 'Could not find revert request with pr# 2048 in repository cocoon to delete.',
- );
- }
- expect(hasError, isTrue);
- });
-
- test('Delete revert request record handles success but wrong number of affected rows.', () async {
- when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) {
- return Future<QueryResponse>.value(
- QueryResponse.fromJson(jsonDecode(insertDeleteUpdateSuccessTooManyRows) as Map<dynamic, dynamic>),
- );
- });
-
- bool hasError = false;
- try {
- await service.deleteRevertRequestRecord(
- projectId: expectedProjectId,
- prNumber: 2048,
- repository: 'cocoon',
- );
- } on BigQueryException catch (exception) {
- hasError = true;
- expect(
- exception.cause,
- 'More than one row was deleted from the database for revert request with pr# 2048 in repository cocoon.',
- );
- }
- expect(hasError, isTrue);
- });
-
- test('Update record is successful.', () async {
- when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) {
- return Future<QueryResponse>.value(
- QueryResponse.fromJson(jsonDecode(insertDeleteUpdateSuccessResponse) as Map<dynamic, dynamic>),
- );
- });
-
- bool hasError = false;
- try {
- await service.updateReviewRequestIssue(
- projectId: expectedProjectId,
- reviewIssueLandedTimestamp: DateTime.now(),
- reviewIssueNumber: 2048,
- reviewIssueClosedBy: 'ricardoamador',
- );
- } on BigQueryException {
- hasError = true;
- }
-
- expect(hasError, isFalse);
- });
-
- test('Update revert request review record is successful but wrong number of rows is updated.', () async {
- when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) {
- return Future<QueryResponse>.value(
- QueryResponse.fromJson(jsonDecode(insertDeleteUpdateSuccessTooManyRows) as Map<dynamic, dynamic>),
- );
- });
-
- bool hasError = false;
- try {
- await service.updateReviewRequestIssue(
- projectId: expectedProjectId,
- reviewIssueLandedTimestamp: DateTime.now(),
- reviewIssueNumber: 2048,
- reviewIssueClosedBy: 'ricardoamador',
- );
- } on BigQueryException catch (exception) {
- hasError = true;
- expect(
- exception.cause,
- 'There was an error updating revert request record review issue landed timestamp with review issue number 2048.',
- );
- }
-
- expect(hasError, isTrue);
- });
-
- test('Update revert request review record does not complete successfully.', () async {
- when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) {
- return Future<QueryResponse>.value(
- QueryResponse.fromJson(jsonDecode(errorResponse) as Map<dynamic, dynamic>),
- );
- });
-
- bool hasError = false;
- try {
- await service.updateReviewRequestIssue(
- projectId: expectedProjectId,
- reviewIssueLandedTimestamp: DateTime.now(),
- reviewIssueNumber: 2048,
- reviewIssueClosedBy: 'ricardoamador',
- );
- } on BigQueryException catch (exception) {
- hasError = true;
- expect(exception.cause, 'Update of review issue 2048 did not complete.');
- }
-
- expect(hasError, isTrue);
- });
-}
diff --git a/auto_submit/test/service/config_test.dart b/auto_submit/test/service/config_test.dart
deleted file mode 100644
index 88eed7e..0000000
--- a/auto_submit/test/service/config_test.dart
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright 2020 The Flutter 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 'dart:typed_data';
-import 'dart:convert';
-
-import 'package:auto_submit/service/config.dart';
-import 'package:auto_submit/service/secrets.dart';
-import 'package:github/github.dart';
-import 'package:http/testing.dart';
-import 'package:http/http.dart' as http;
-import 'package:neat_cache/cache_provider.dart';
-import 'package:neat_cache/neat_cache.dart';
-import 'package:test/test.dart';
-
-import 'config_test_data.dart';
-
-/// Number of entries allowed in [Cache].
-const int kCacheSize = 1024;
-
-void main() {
- group('Config', () {
- late CacheProvider cacheProvider;
- late Config config;
- late MockClient mockClient;
- final SecretManager secretManager = LocalSecretManager();
- final RepositorySlug flutterSlug = RepositorySlug('flutter', 'flutter');
- final RepositorySlug testSlug = RepositorySlug('test', 'test');
- const int kCacheSize = 1024;
-
- setUp(() {
- cacheProvider = Cache.inMemoryCacheProvider(kCacheSize);
- mockClient = MockClient((_) async => http.Response(installations, HttpStatus.ok));
- config = Config(
- cacheProvider: cacheProvider,
- httpProvider: () => mockClient,
- secretManager: secretManager,
- );
- });
-
- test('verify github App Installation Id ', () async {
- final Uri githubInstallationUri = Uri.https('api.github.com', 'app/installations');
- final http.Response response = await mockClient.get(githubInstallationUri);
- final List<dynamic> list =
- (json.decode(response.body).map((dynamic data) => (data) as Map<String, dynamic>)).toList() as List<dynamic>;
- expect(list[0]['id'].toString(), '24369313');
- expect(list[1]['id'].toString(), '23587612');
- });
-
- test('generateGithubToken pulls from cache', () async {
- const String configValue = 'githubToken';
- final Uint8List cachedValue = Uint8List.fromList(configValue.codeUnits);
- final Cache cache = Cache<dynamic>(cacheProvider).withPrefix('config');
- await cache['githubToken-${flutterSlug.owner}'].set(
- cachedValue,
- const Duration(minutes: 1),
- );
-
- final String githubToken = await config.generateGithubToken(flutterSlug);
- expect(githubToken, configValue);
- });
-
- test('Github clients are created with correct token', () async {
- const String flutterToken = 'flutterToken';
- final Uint8List flutterValue = Uint8List.fromList(flutterToken.codeUnits);
- const String testToken = 'testToken';
- final Uint8List testValue = Uint8List.fromList(testToken.codeUnits);
- final Cache cache = Cache<dynamic>(cacheProvider).withPrefix('config');
- await cache['githubToken-${flutterSlug.owner}'].set(
- flutterValue,
- const Duration(minutes: 1),
- );
- await cache['githubToken-${testSlug.owner}'].set(
- testValue,
- const Duration(minutes: 1),
- );
-
- final GitHub flutterClient = await config.createGithubClient(flutterSlug);
- final GitHub testClient = await config.createGithubClient(testSlug);
- expect(flutterClient.auth.token!, flutterToken);
- expect(testClient.auth.token!, testToken);
- });
- });
-}
diff --git a/auto_submit/test/service/config_test_data.dart b/auto_submit/test/service/config_test_data.dart
deleted file mode 100644
index c644a50..0000000
--- a/auto_submit/test/service/config_test_data.dart
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2022 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-const String installations = '''
- [
- {
- "id": 24369313,
- "account": {
- "login": "flutter",
- "id": 14101776,
- "node_id": "MDEyOk9yZ2FuaXphdGlvbjE0MTAxNzc2",
- "avatar_url": "https://avatars.githubusercontent.com/u/14101776?v=4"
- },
- "repository_selection": "selected",
- "app_id": 168178,
- "app_slug": "auto-submit",
- "target_id": 14101776,
- "target_type": "Organization",
- "permissions": {
- "checks": "read",
- "metadata": "read",
- "single_file": "read",
- "pull_requests": "read"
- },
- "events": [
- "label",
- "pull_request"
- ],
- "created_at": "2022-03-23T23:00:37.000Z",
- "updated_at": "2022-03-23T23:00:37.000Z",
- "single_file_name": ".github/autosubmit.yml",
- "has_multiple_single_files": false,
- "single_file_paths": [
- ".github/autosubmit.yml"
- ]
- },
- {
- "id": 23587612,
- "account": {
- "login": "CaseyHillers",
- "id": 2148558,
- "node_id": "MDQ6VXNlcjIxNDg1NTg=",
- "avatar_url": "https://avatars.githubusercontent.com/u/2148558?v=4"
- },
- "repository_selection": "selected",
- "app_id": 168178,
- "app_slug": "auto-submit",
- "target_id": 2148558,
- "target_type": "User",
- "permissions": {
- "checks": "read",
- "metadata": "read",
- "single_file": "read",
- "pull_requests": "read"
- },
- "events": [
- "label",
- "pull_request"
- ],
- "created_at": "2022-02-24T18:29:28.000Z",
- "updated_at": "2022-03-23T22:18:46.000Z",
- "single_file_name": ".github/autosubmit.yml",
- "has_multiple_single_files": false,
- "single_file_paths": [
- ".github/autosubmit.yml"
- ]
- }
-]''';
diff --git a/auto_submit/test/service/github_service_test.dart b/auto_submit/test/service/github_service_test.dart
deleted file mode 100644
index 6bfd46b..0000000
--- a/auto_submit/test/service/github_service_test.dart
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2022 The Flutter 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:convert';
-
-import 'package:auto_submit/service/github_service.dart';
-import 'package:github/github.dart';
-import 'package:mockito/mockito.dart';
-import 'package:test/test.dart';
-
-import '../requests/github_webhook_test_data.dart';
-import '../utilities/mocks.dart';
-
-void main() {
- late GithubService githubService;
- late RepositorySlug slug;
- late RepositoryCommit testCommit;
- final MockGitHub mockGitHub = MockGitHub();
- final MockRepositoriesService mockRepositoriesService = MockRepositoriesService();
- final MockGitHubComparison mockGitHubComparison = MockGitHubComparison();
- final MockResponse mockResponse = MockResponse();
-
- const String author = '''{"login": "octocat", "id": 1}''';
- const String url = 'testUrl';
- const String sha = '6dcb09b5b57875f334f61aebed695e2e4193db5e';
-
- setUp(() {
- githubService = GithubService(mockGitHub);
- slug = RepositorySlug('flutter', 'cocoon');
- testCommit = RepositoryCommit.fromJson(
- jsonDecode('{"url": "$url", "author": $author, "sha": "$sha"}') as Map<String, dynamic>,
- );
-
- when(mockGitHubComparison.behindBy).thenReturn(10);
- when(
- mockGitHub.request(
- any,
- any,
- headers: anyNamed('headers'),
- params: anyNamed('params'),
- body: anyNamed('body'),
- statusCode: anyNamed('statusCode'),
- fail: anyNamed('fail'),
- preview: anyNamed('preview'),
- ),
- ).thenAnswer((_) => Future.value(mockResponse));
- when(mockResponse.statusCode).thenReturn(200);
- when(mockGitHub.repositories).thenReturn(mockRepositoriesService);
- when(mockRepositoriesService.getCommit(any, any)).thenAnswer((_) => Future.value(testCommit));
- when(mockRepositoriesService.compareCommits(any, any, any)).thenAnswer((_) => Future.value(mockGitHubComparison));
- });
-
- test('listReviews retrieves all reviews of the pull request', () async {
- final RepositoryCommit commit = await githubService.getCommit(slug, sha);
- expect(commit.author!.login, 'octocat');
- expect(commit.url, 'testUrl');
- expect(commit.sha, '6dcb09b5b57875f334f61aebed695e2e4193db5e');
- });
-
- test('Merges branch', () async {
- when(mockGitHubComparison.behindBy).thenReturn(10);
- final PullRequest pullRequest = generatePullRequest();
- await githubService.autoMergeBranch(pullRequest);
- verify(mockResponse.statusCode).called(1);
- });
-
- test('Does not merge branch', () async {
- when(mockGitHubComparison.behindBy).thenReturn(9);
- final PullRequest pullRequest = generatePullRequest();
- await githubService.autoMergeBranch(pullRequest);
- verifyNever(mockResponse.statusCode);
- });
-}
diff --git a/auto_submit/test/service/pull_request_validation_service_test.dart b/auto_submit/test/service/pull_request_validation_service_test.dart
deleted file mode 100644
index 35eece3..0000000
--- a/auto_submit/test/service/pull_request_validation_service_test.dart
+++ /dev/null
@@ -1,446 +0,0 @@
-// Copyright 2023 The Flutter 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:async';
-import 'dart:convert';
-
-import 'package:auto_submit/configuration/repository_configuration.dart';
-import 'package:auto_submit/model/auto_submit_query_result.dart' as auto hide PullRequest;
-import 'package:auto_submit/service/pull_request_validation_service.dart';
-import 'package:auto_submit/service/validation_service.dart';
-import 'package:github/github.dart';
-import 'package:googleapis/bigquery/v2.dart';
-import 'package:mockito/mockito.dart';
-import 'package:retry/retry.dart';
-import 'package:test/test.dart';
-
-import '../configuration/repository_configuration_data.dart';
-import '../requests/github_webhook_test_data.dart';
-import '../src/request_handling/fake_pubsub.dart';
-import '../src/service/fake_bigquery_service.dart';
-import '../src/service/fake_config.dart';
-import '../src/service/fake_graphql_client.dart';
-import '../src/service/fake_github_service.dart';
-import '../utilities/utils.dart';
-import '../utilities/mocks.dart';
-import 'bigquery_test.dart';
-
-void main() {
- late PullRequestValidationService validationService;
- late FakeConfig config;
- late FakeGithubService githubService;
- late FakeGraphQLClient githubGraphQLClient;
- late RepositorySlug slug;
-
- late MockJobsResource jobsResource;
- late FakeBigqueryService bigqueryService;
-
- setUp(() {
- githubGraphQLClient = FakeGraphQLClient();
- githubService = FakeGithubService(client: MockGitHub());
- config = FakeConfig(githubService: githubService, githubGraphQLClient: githubGraphQLClient);
- validationService = PullRequestValidationService(
- config,
- retryOptions: const RetryOptions(delayFactor: Duration.zero, maxDelay: Duration.zero, maxAttempts: 1),
- );
- slug = RepositorySlug('flutter', 'cocoon');
-
- jobsResource = MockJobsResource();
- bigqueryService = FakeBigqueryService(jobsResource);
- config.bigqueryService = bigqueryService;
- config.repositoryConfigurationMock = RepositoryConfiguration.fromYaml(sampleConfigNoOverride);
-
- when(jobsResource.query(captureAny, any)).thenAnswer((Invocation invocation) {
- return Future<QueryResponse>.value(
- QueryResponse.fromJson(jsonDecode(insertDeleteUpdateSuccessResponse) as Map<dynamic, dynamic>),
- );
- });
- });
-
- test('Leaves label and no comment when no approval if both parties are members', () async {
- final PullRequestHelper flutterRequest = PullRequestHelper(
- prNumber: 0,
- lastCommitHash: oid,
- reviews: <PullRequestReviewHelper>[],
- );
- githubService.checkRunsData = checkRunsMock;
- githubService.createCommentData = createCommentMock;
- githubService.isTeamMemberMockMap['author1'] = true;
- githubService.isTeamMemberMockMap['member'] = true;
- final FakePubSub pubsub = FakePubSub();
- final PullRequest pullRequest = generatePullRequest(prNumber: 0, repoName: slug.name);
- githubService.pullRequestData = pullRequest;
- unawaited(pubsub.publish('auto-submit-queue-sub', pullRequest));
- final auto.QueryResult queryResult = createQueryResult(flutterRequest);
-
- await validationService.processPullRequest(
- config: config,
- result: queryResult,
- messagePullRequest: pullRequest,
- ackId: 'test',
- pubsub: pubsub,
- );
-
- expect(githubService.issueComment, isNull);
- expect(githubService.labelRemoved, false);
- assert(pubsub.messagesQueue.isNotEmpty);
- });
-
- // This tests for valid pull request into not default base branch which
- // will ignore the tree status as it does not matter.
- test('Processes successfully when base branch is not default', () async {
- final PullRequestHelper flutterRequest = PullRequestHelper(
- prNumber: 0,
- lastCommitHash: oid,
- reviews: <PullRequestReviewHelper>[
- const PullRequestReviewHelper(
- authorName: 'member',
- state: ReviewState.APPROVED,
- memberType: MemberType.OWNER,
- ),
- ],
- );
- githubService.checkRunsData = checkRunsMock;
- githubService.checkRunsMock = checkRunsMock;
- githubService.createCommentData = createCommentMock;
- githubService.isTeamMemberMockMap['author1'] = true;
- githubService.isTeamMemberMockMap['member'] = true;
- final FakePubSub pubsub = FakePubSub();
- final PullRequest pullRequest = generatePullRequest(
- prNumber: 0,
- repoName: slug.name,
- baseRef: 'feature_a',
- mergeable: true,
- );
- unawaited(pubsub.publish('auto-submit-queue-sub', pullRequest));
- final auto.QueryResult queryResult = createQueryResult(flutterRequest);
- githubService.pullRequestMock = pullRequest;
- githubService.mergeRequestMock = PullRequestMerge(
- merged: true,
- sha: 'asdfioefmasdf',
- message: 'Merged successfully.',
- );
-
- await validationService.processPullRequest(
- config: config,
- result: queryResult,
- messagePullRequest: pullRequest,
- ackId: 'test',
- pubsub: pubsub,
- );
-
- // These checks indicate that the pull request has been merged as the label
- // is not removed and there was no issue coment generated and the message
- // was acknowledged.
- expect(githubService.issueComment, isNull);
- expect(githubService.labelRemoved, false);
- assert(pubsub.messagesQueue.isEmpty);
- });
-
- // This tests for valid pull request where tree status was not ready for
- // processing, meaning no issueComment was created and the 'autosubmit' label
- // is not removed and we do not ack the message.
- test('Processing fails when base branch is default with no statuses', () async {
- final PullRequestHelper flutterRequest = PullRequestHelper(
- prNumber: 0,
- lastCommitHash: oid,
- reviews: <PullRequestReviewHelper>[
- const PullRequestReviewHelper(
- authorName: 'member',
- state: ReviewState.APPROVED,
- memberType: MemberType.OWNER,
- ),
- ],
- lastCommitStatuses: null,
- );
- githubService.checkRunsData = checkRunsMock;
- githubService.createCommentData = createCommentMock;
- githubService.isTeamMemberMockMap['author1'] = true;
- githubService.isTeamMemberMockMap['member'] = true;
- final FakePubSub pubsub = FakePubSub();
- final PullRequest pullRequest = generatePullRequest(prNumber: 0);
- githubService.pullRequestData = pullRequest;
- unawaited(pubsub.publish('auto-submit-queue-sub', pullRequest));
- final auto.QueryResult queryResult = createQueryResult(flutterRequest);
-
- await validationService.processPullRequest(
- config: config,
- result: queryResult,
- messagePullRequest: pullRequest,
- ackId: 'test',
- pubsub: pubsub,
- );
-
- expect(githubService.issueComment, isNull);
- expect(githubService.labelRemoved, false);
- assert(pubsub.messagesQueue.isNotEmpty);
- });
-
- group('Process pull request method tests', () {
- test('Should process message when autosubmit label exists and pr is open', () async {
- final PullRequest pullRequest = generatePullRequest(prNumber: 0, repoName: slug.name);
- githubService.pullRequestData = pullRequest;
- expect(await validationService.shouldProcess(pullRequest), true);
- });
-
- test('Skip processing message when autosubmit label does not exist anymore', () async {
- final PullRequest pullRequest = generatePullRequest(prNumber: 0, repoName: slug.name);
- pullRequest.labels = <IssueLabel>[];
- githubService.pullRequestData = pullRequest;
- expect(await validationService.shouldProcess(pullRequest), false);
- });
-
- test('Skip processing message when the pull request is closed', () async {
- final PullRequest pullRequest = generatePullRequest(prNumber: 0, repoName: slug.name);
- pullRequest.state = 'closed';
- githubService.pullRequestData = pullRequest;
- expect(await validationService.shouldProcess(pullRequest), false);
- });
-
- test('Should not process message when revert label exists and pr is open', () async {
- final PullRequest pullRequest = generatePullRequest(prNumber: 0, repoName: slug.name);
- final IssueLabel issueLabel = IssueLabel(name: 'revert');
- pullRequest.labels = <IssueLabel>[issueLabel];
- githubService.pullRequestData = pullRequest;
- expect(await validationService.shouldProcess(pullRequest), false);
- });
-
- test('Skip processing message when revert label exists and pr is closed', () async {
- final PullRequest pullRequest = generatePullRequest(prNumber: 0, repoName: slug.name);
- pullRequest.state = 'closed';
- final IssueLabel issueLabel = IssueLabel(name: 'revert');
- pullRequest.labels = <IssueLabel>[issueLabel];
- githubService.pullRequestData = pullRequest;
- expect(await validationService.shouldProcess(pullRequest), false);
- });
- });
-
- group('processMerge', () {
- test('Correct PR titles when merging to use Reland', () async {
- final PullRequest pullRequest = generatePullRequest(
- prNumber: 0,
- repoName: slug.name,
- title: 'Revert "Revert "My first PR!"',
- mergeable: true,
- );
- githubService.pullRequestData = pullRequest;
- githubService.mergeRequestMock = PullRequestMerge(
- merged: true,
- sha: pullRequest.mergeCommitSha,
- );
-
- final MergeResult result = await validationService.processMerge(
- config: config,
- messagePullRequest: pullRequest,
- );
-
- expect(result.message, contains('Reland "My first PR!"'));
- });
-
- test('Removes label and post comment when no approval for non-flutter hacker', () async {
- final PullRequestHelper flutterRequest = PullRequestHelper(
- prNumber: 0,
- lastCommitHash: oid,
- reviews: <PullRequestReviewHelper>[],
- );
- githubService.checkRunsData = checkRunsMock;
- githubService.createCommentData = createCommentMock;
- githubService.isTeamMemberMockMap['author1'] = false;
- githubService.isTeamMemberMockMap['member'] = true;
- final FakePubSub pubsub = FakePubSub();
- final PullRequest pullRequest = generatePullRequest(prNumber: 0, repoName: slug.name);
- githubService.pullRequestData = pullRequest;
- unawaited(pubsub.publish('auto-submit-queue-sub', pullRequest));
- final auto.QueryResult queryResult = createQueryResult(flutterRequest);
-
- await validationService.processPullRequest(
- config: config,
- result: queryResult,
- messagePullRequest: pullRequest,
- ackId: 'test',
- pubsub: pubsub,
- );
-
- expect(githubService.issueComment, isNotNull);
- expect(githubService.labelRemoved, true);
- assert(pubsub.messagesQueue.isEmpty);
- });
-
- // This tests for valid pull request where tree status was not ready for
- // processing, meaning no issueComment was created and the 'autosubmit' label
- // is not removed and we do not ack the message.
- test('Processing fails when base branch is default with no statuses', () async {
- final PullRequestHelper flutterRequest = PullRequestHelper(
- prNumber: 0,
- lastCommitHash: oid,
- reviews: <PullRequestReviewHelper>[
- const PullRequestReviewHelper(
- authorName: 'member',
- state: ReviewState.APPROVED,
- memberType: MemberType.OWNER,
- ),
- ],
- lastCommitStatuses: null,
- );
- githubService.checkRunsData = checkRunsMock;
- githubService.createCommentData = createCommentMock;
- githubService.isTeamMemberMockMap['author1'] = true;
- githubService.isTeamMemberMockMap['member'] = true;
- final FakePubSub pubsub = FakePubSub();
- final PullRequest pullRequest = generatePullRequest(prNumber: 0);
- githubService.pullRequestData = pullRequest;
- unawaited(pubsub.publish('auto-submit-queue-sub', pullRequest));
- final auto.QueryResult queryResult = createQueryResult(flutterRequest);
-
- await validationService.processPullRequest(
- config: config,
- result: queryResult,
- messagePullRequest: pullRequest,
- ackId: 'test',
- pubsub: pubsub,
- );
-
- expect(githubService.issueComment, isNull);
- expect(githubService.labelRemoved, false);
- assert(pubsub.messagesQueue.isNotEmpty);
- });
-
- test('Processes successfully when base branch is not default', () async {
- final PullRequestHelper flutterRequest = PullRequestHelper(
- prNumber: 0,
- lastCommitHash: oid,
- reviews: <PullRequestReviewHelper>[
- const PullRequestReviewHelper(
- authorName: 'member',
- state: ReviewState.APPROVED,
- memberType: MemberType.OWNER,
- ),
- ],
- );
- githubService.checkRunsData = checkRunsMock;
- githubService.checkRunsMock = checkRunsMock;
- githubService.createCommentData = createCommentMock;
- githubService.isTeamMemberMockMap['author1'] = true;
- githubService.isTeamMemberMockMap['member'] = true;
- final FakePubSub pubsub = FakePubSub();
- final PullRequest pullRequest = generatePullRequest(
- prNumber: 0,
- repoName: slug.name,
- baseRef: 'feature_a',
- mergeable: true,
- );
- unawaited(pubsub.publish('auto-submit-queue-sub', pullRequest));
- final auto.QueryResult queryResult = createQueryResult(flutterRequest);
- githubService.pullRequestMock = pullRequest;
- githubService.mergeRequestMock = PullRequestMerge(
- merged: true,
- sha: 'asdfioefmasdf',
- message: 'Merged successfully.',
- );
-
- await validationService.processPullRequest(
- config: config,
- result: queryResult,
- messagePullRequest: pullRequest,
- ackId: 'test',
- pubsub: pubsub,
- );
-
- // These checks indicate that the pull request has been merged as the label
- // is not removed and there was no issue comment generated and the message
- // was acknowledged.
- expect(githubService.issueComment, isNull);
- expect(githubService.labelRemoved, false);
- assert(pubsub.messagesQueue.isEmpty);
- });
-
- test('includes PR description in commit message', () async {
- final PullRequest pullRequest = generatePullRequest(
- prNumber: 0,
- repoName: slug.name,
- title: 'PR title',
- // The test-only helper function `generatePullRequest` will interpolate
- // this string into a JSON string which will then be decoded--thus, this string must be
- // a valid JSON substring, with escaped newlines.
- body: r'PR description\nwhich\nis multiline.',
- mergeable: true,
- );
- githubService.pullRequestData = pullRequest;
- githubService.mergeRequestMock = PullRequestMerge(
- merged: true,
- sha: pullRequest.mergeCommitSha,
- );
- final MergeResult result = await validationService.processMerge(
- config: config,
- messagePullRequest: pullRequest,
- );
-
- expect(result.message, '''
-PR description
-which
-is multiline.''');
- });
-
- test('commit message filters out markdown checkboxes', () async {
- const String prTitle = 'Important update #4';
- const String prBody = '''
-Various bugfixes and performance improvements.
-
-Fixes #12345 and #3.
-This is the second line in a paragraph.
-
-## Pre-launch Checklist
-
-- [ ] I read the [Contributor Guide] and followed the process outlined there for submitting PRs.
-- [ ] I read the [Tree Hygiene] wiki page, which explains my responsibilities.
-- [ ] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement].
-- [x] I signed the [CLA].
-- [ ] I listed at least one issue that this PR fixes in the description above.
-- [ ] I updated/added relevant documentation (doc comments with `///`).
-- [X] I added new tests to check the change I am making, or this PR is [test-exempt].
-- [ ] All existing and new tests are passing.
-
-If you need help, consider asking for advice on the #hackers-new channel on [Discord].
-
-<!-- Links -->
-[Contributor Guide]: https://github.com/flutter/flutter/wiki/Tree-hygiene#overview
-[Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene
-[test-exempt]: https://github.com/flutter/flutter/wiki/Tree-hygiene#tests
-[Flutter Style Guide]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo
-[Features we expect every widget to implement]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#features-we-expect-every-widget-to-implement
-[CLA]: https://cla.developers.google.com/
-[flutter/tests]: https://github.com/flutter/tests
-[breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes
-[Discord]: https://github.com/flutter/flutter/wiki/Chat''';
-
- final PullRequest pullRequest = generatePullRequest(
- prNumber: 0,
- repoName: slug.name,
- title: prTitle,
- // The test-only helper function `generatePullRequest` will interpolate
- // this string into a JSON string which will then be decoded--thus, this string must be
- // a valid JSON substring, with escaped newlines.
- body: prBody.replaceAll('\n', r'\n'),
- mergeable: true,
- );
- githubService.pullRequestData = pullRequest;
- githubService.mergeRequestMock = PullRequestMerge(
- merged: true,
- sha: pullRequest.mergeCommitSha,
- );
-
- final MergeResult result = await validationService.processMerge(
- config: config,
- messagePullRequest: pullRequest,
- );
-
- expect(result.result, isTrue);
- expect(result.message, '''
-Various bugfixes and performance improvements.
-
-Fixes #12345 and #3.
-This is the second line in a paragraph.''');
- });
- });
-}
diff --git a/auto_submit/test/service/revert_issue_body_formatter_test.dart b/auto_submit/test/service/revert_issue_body_formatter_test.dart
deleted file mode 100644
index 823914f..0000000
--- a/auto_submit/test/service/revert_issue_body_formatter_test.dart
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2023 The Flutter 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 'package:auto_submit/service/revert_issue_body_formatter.dart';
-import 'package:github/github.dart';
-import 'package:test/test.dart';
-
-void main() {
- // Calls are made as they are done in git_cli_revert_method.dart.
- test('Allow nullable fields in formatter.', () {
- final PullRequest pullRequest = PullRequest(number: 123456, body: null, title: 'Interesting title.');
- const String sender = 'RevertAuthor';
- RevertIssueBodyFormatter? revertIssueBodyFormatter;
- expect(
- () => revertIssueBodyFormatter = RevertIssueBodyFormatter(
- slug: RepositorySlug('flutter', 'flutter'),
- originalPrNumber: pullRequest.number!,
- initiatingAuthor: sender,
- originalPrTitle: pullRequest.title,
- originalPrBody: pullRequest.body,
- ),
- returnsNormally,
- );
- revertIssueBodyFormatter!.format;
- expect(revertIssueBodyFormatter, isNotNull);
- expect(revertIssueBodyFormatter!.revertPrBody!.contains('No description provided.'), isTrue);
- });
-}
diff --git a/auto_submit/test/service/revert_request_validation_service_test.dart b/auto_submit/test/service/revert_request_validation_service_test.dart
deleted file mode 100644
index d2dbc9b..0000000
--- a/auto_submit/test/service/revert_request_validation_service_test.dart
+++ /dev/null
@@ -1,1141 +0,0 @@
-// Copyright 2023 The Flutter 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:async';
-import 'dart:convert';
-
-import 'package:auto_submit/configuration/repository_configuration.dart';
-import 'package:auto_submit/model/auto_submit_query_result.dart' as auto hide PullRequest;
-import 'package:auto_submit/requests/github_pull_request_event.dart';
-import 'package:auto_submit/service/revert_request_validation_service.dart';
-import 'package:auto_submit/service/validation_service.dart';
-import 'package:auto_submit/validations/validation.dart';
-import 'package:github/github.dart';
-import 'package:googleapis/bigquery/v2.dart';
-import 'package:mockito/mockito.dart';
-import 'package:retry/retry.dart';
-import 'package:test/test.dart';
-
-import '../configuration/repository_configuration_data.dart';
-import '../requests/github_webhook_test_data.dart';
-import '../src/action/fake_revert_method.dart';
-import '../src/request_handling/fake_pubsub.dart';
-import '../src/service/fake_approver_service.dart';
-import '../src/service/fake_bigquery_service.dart';
-import '../src/service/fake_config.dart';
-import '../src/service/fake_graphql_client.dart';
-import '../src/service/fake_github_service.dart';
-import '../src/validations/fake_approval.dart';
-import '../src/validations/fake_mergeable.dart';
-import '../src/validations/fake_required_check_runs.dart';
-import '../src/validations/fake_validation_filter.dart';
-import '../utilities/utils.dart';
-import '../utilities/mocks.dart';
-import 'bigquery_test.dart';
-
-void main() {
- late RevertRequestValidationService validationService;
- late FakeConfig config;
- late FakeGithubService githubService;
- late FakeGraphQLClient githubGraphQLClient;
- late RepositorySlug slug;
-
- late MockJobsResource jobsResource;
- late FakeBigqueryService bigqueryService;
- late FakeRevertMethod revertMethod;
-
- setUp(() {
- githubGraphQLClient = FakeGraphQLClient();
- githubService = FakeGithubService(client: MockGitHub());
- config = FakeConfig(githubService: githubService, githubGraphQLClient: githubGraphQLClient);
- revertMethod = FakeRevertMethod();
- validationService = RevertRequestValidationService(
- config,
- retryOptions: const RetryOptions(delayFactor: Duration.zero, maxDelay: Duration.zero, maxAttempts: 1),
- revertMethod: revertMethod,
- );
- slug = RepositorySlug('flutter', 'cocoon');
- jobsResource = MockJobsResource();
- bigqueryService = FakeBigqueryService(jobsResource);
- config.bigqueryService = bigqueryService;
- config.repositoryConfigurationMock = RepositoryConfiguration.fromYaml(sampleConfigNoOverride);
-
- when(jobsResource.query(captureAny, any)).thenAnswer((Invocation invocation) {
- return Future<QueryResponse>.value(
- QueryResponse.fromJson(jsonDecode(insertDeleteUpdateSuccessResponse) as Map<dynamic, dynamic>),
- );
- });
- });
-
- group('Testing time limit check:', () {
- test('Pull request is rejected if merged over 24 hours ago.', () {
- final PullRequest pullRequest = generatePullRequest(
- prNumber: 0,
- repoName: slug.name,
- mergedAt: DateTime.now().subtract(const Duration(hours: 25)),
- );
- expect(validationService.isWithinTimeLimit(pullRequest), isFalse);
- });
-
- test('Pull request is rejected if mergedAt is null', () {
- final PullRequest pullRequest = PullRequest(
- number: 0,
- base: PullRequestHead(repo: Repository(name: slug.name)),
- mergedAt: null,
- );
-
- expect(validationService.isWithinTimeLimit(pullRequest), isFalse);
- });
-
- test('Pull request is accepted if mergedAt is within 24 hours ago.', () {
- final PullRequest pullRequest = generatePullRequest(
- prNumber: 0,
- repoName: slug.name,
- mergedAt: DateTime.now().subtract(const Duration(hours: 23)),
- );
-
- expect(validationService.isWithinTimeLimit(pullRequest), isTrue);
- });
-
- test('Pull request is accepted if mergedAt is exactly 24 hours ago.', () {
- final PullRequest pullRequest = generatePullRequest(
- prNumber: 0,
- repoName: slug.name,
- mergedAt: DateTime.now().subtract(const Duration(hours: 24)),
- );
-
- expect(validationService.isWithinTimeLimit(pullRequest), isTrue);
- });
- });
-
- group('shouldProcess:', () {
- test('Process revert from closed as "revert"', () async {
- final PullRequest pullRequest = generatePullRequest(prNumber: 0, repoName: slug.name, state: 'closed');
- final IssueLabel issueLabel = IssueLabel(name: 'revert');
- final List<String> labelNames = ['revert'];
- pullRequest.labels = <IssueLabel>[issueLabel];
- githubService.pullRequestData = pullRequest;
- final RevertProcessMethod revertProcessMethod = await validationService.shouldProcess(pullRequest, labelNames);
-
- expect(revertProcessMethod, RevertProcessMethod.revert);
- });
-
- test('Process open revert request as "revert of"', () async {
- final PullRequest pullRequest =
- generatePullRequest(prNumber: 0, repoName: slug.name, state: 'open', author: config.autosubmitBot);
- final IssueLabel issueLabel = IssueLabel(name: 'revert of');
- final List<String> labelNames = ['revert of'];
- pullRequest.labels = <IssueLabel>[issueLabel];
- githubService.pullRequestData = pullRequest;
- final RevertProcessMethod revertProcessMethod = await validationService.shouldProcess(pullRequest, labelNames);
-
- expect(revertProcessMethod, RevertProcessMethod.revertOf);
- });
-
- test('Pull request state is open with revert label is not processed', () async {
- final PullRequest pullRequest = generatePullRequest(prNumber: 0, repoName: slug.name, state: 'open');
- final IssueLabel issueLabel = IssueLabel(name: 'revert');
- final List<String> labelNames = ['revert'];
- pullRequest.labels = <IssueLabel>[issueLabel];
- githubService.pullRequestData = pullRequest;
- final RevertProcessMethod revertProcessMethod = await validationService.shouldProcess(pullRequest, labelNames);
-
- expect(revertProcessMethod, RevertProcessMethod.none);
- });
-
- test('Pull request is closed with "revert of" label is not processed', () async {
- final PullRequest pullRequest =
- generatePullRequest(prNumber: 0, repoName: slug.name, state: 'closed', author: config.autosubmitBot);
- final IssueLabel issueLabel = IssueLabel(name: 'revert of');
- final List<String> labelNames = ['revert of'];
- pullRequest.labels = <IssueLabel>[issueLabel];
- githubService.pullRequestData = pullRequest;
- final RevertProcessMethod revertProcessMethod = await validationService.shouldProcess(pullRequest, labelNames);
-
- expect(revertProcessMethod, RevertProcessMethod.none);
- });
-
- test('"revert of" pull request not authored by autosubmit bot is not processed.', () async {
- final PullRequest pullRequest =
- generatePullRequest(prNumber: 0, repoName: slug.name, state: 'open', author: 'octocat');
- final IssueLabel issueLabel = IssueLabel(name: 'revert of');
- final List<String> labelNames = ['revert of'];
- pullRequest.labels = <IssueLabel>[issueLabel];
- githubService.pullRequestData = pullRequest;
- final RevertProcessMethod revertProcessMethod = await validationService.shouldProcess(pullRequest, labelNames);
-
- expect(revertProcessMethod, RevertProcessMethod.none);
- });
-
- test('Closed pull request not processed if it was not merged', () async {
- final PullRequest pullRequest = PullRequest(
- number: 0,
- base: PullRequestHead(repo: Repository(name: slug.name)),
- state: 'closed',
- mergedAt: null,
- );
- final IssueLabel issueLabel = IssueLabel(name: 'revert');
- final List<String> labelNames = ['revert'];
- pullRequest.labels = <IssueLabel>[issueLabel];
- githubService.pullRequestData = pullRequest;
- final RevertProcessMethod revertProcessMethod = await validationService.shouldProcess(pullRequest, labelNames);
-
- expect(revertProcessMethod, RevertProcessMethod.none);
- });
- });
-
- group('Process revert pull requests:', () {
- test('Remove label and post comment when issue has passed time limit to be reverted.', () async {
- // setup objects
- final FakePubSub pubsub = FakePubSub();
-
- final PullRequestHelper flutterRequest = PullRequestHelper(
- prNumber: 0,
- lastCommitHash: oid,
- reviews: <PullRequestReviewHelper>[],
- mergedAt: DateTime.now().subtract(const Duration(hours: 25)),
- );
-
- final PullRequest pullRequest = generatePullRequest(
- prNumber: 0,
- repoName: slug.name,
- mergedAt: DateTime.now().subtract(const Duration(hours: 25)),
- );
-
- final auto.QueryResult queryResult = createQueryResult(flutterRequest);
-
- final GithubPullRequestEvent githubPullRequestEvent = GithubPullRequestEvent(
- pullRequest: pullRequest,
- action: 'labeled',
- sender: User(login: 'ricardoamador'),
- );
-
- // setup fields
- githubService.createCommentData = createCommentMock;
- githubService.pullRequestMock = pullRequest;
-
- // run tests
- unawaited(pubsub.publish(config.pubsubRevertRequestSubscription, pullRequest));
- await validationService.processRevertRequest(
- result: queryResult,
- githubPullRequestEvent: githubPullRequestEvent,
- ackId: 'test',
- pubsub: pubsub,
- );
-
- // validate
- expect(githubService.issueComment, isNotNull);
- expect(githubService.labelRemoved, true);
- assert(pubsub.messagesQueue.isEmpty);
- });
-
- test('Create the new revert issue from the closed one.', () async {
- // setup
- final FakePubSub pubsub = FakePubSub();
-
- final PullRequestHelper flutterRequest = PullRequestHelper(
- prNumber: 0,
- lastCommitHash: oid,
- reviews: <PullRequestReviewHelper>[],
- );
-
- final PullRequest pullRequest = generatePullRequest(prNumber: 0, repoName: slug.name, author: 'auto-submit[bot]');
-
- final auto.QueryResult queryResult = createQueryResult(flutterRequest);
-
- final GithubPullRequestEvent githubPullRequestEvent = GithubPullRequestEvent(
- pullRequest: pullRequest,
- action: 'labeled',
- sender: User(login: 'auto-submit[bot]'),
- );
-
- // setup fields
- githubService.createCommentData = createCommentMock;
- githubService.pullRequestMock = pullRequest;
- revertMethod.object = pullRequest;
-
- // run test
- unawaited(pubsub.publish(config.pubsubRevertRequestSubscription, pullRequest));
- await validationService.processRevertRequest(
- result: queryResult,
- githubPullRequestEvent: githubPullRequestEvent,
- ackId: 'test',
- pubsub: pubsub,
- );
-
- // validate
- expect(githubService.issueComment, isNull);
- expect(githubService.labelRemoved, true);
- assert(pubsub.messagesQueue.isEmpty);
- });
-
- test('New revert request is not created, label is removed.', () async {
- // setup
- final FakePubSub pubsub = FakePubSub();
-
- final PullRequestHelper flutterRequest = PullRequestHelper(
- prNumber: 0,
- lastCommitHash: oid,
- reviews: <PullRequestReviewHelper>[],
- );
-
- final PullRequest pullRequest = generatePullRequest(
- prNumber: 0,
- repoName: slug.name,
- );
-
- final auto.QueryResult queryResult = createQueryResult(flutterRequest);
-
- final GithubPullRequestEvent githubPullRequestEvent = GithubPullRequestEvent(
- pullRequest: pullRequest,
- action: 'labeled',
- sender: User(login: 'ricardoamador'),
- );
-
- // setup fields
- githubService.createCommentData = createCommentMock;
- revertMethod.throwException = true;
- revertMethod.object = queryResult.repository!.pullRequest;
- githubService.pullRequestMock = pullRequest;
-
- // run test
- unawaited(pubsub.publish(config.pubsubRevertRequestSubscription, pullRequest));
- await validationService.processRevertRequest(
- result: queryResult,
- githubPullRequestEvent: githubPullRequestEvent,
- ackId: 'test',
- pubsub: pubsub,
- );
-
- // validate
- expect(githubService.issueComment, isNotNull);
- expect(githubService.labelRemoved, true);
- assert(pubsub.messagesQueue.isEmpty);
- });
- });
-
- group('Process "revert of" pull requests:', () {
- test('Pull request is not processed due to repo config', () async {
- // setup
- config.repositoryConfigurationMock = RepositoryConfiguration.fromYaml(sampleConfigRevertReviewRequired);
- final FakePubSub pubsub = FakePubSub();
-
- final PullRequestHelper flutterRequest = PullRequestHelper(
- prNumber: 0,
- lastCommitHash: oid,
- reviews: <PullRequestReviewHelper>[],
- );
-
- final auto.QueryResult queryResult = createQueryResult(flutterRequest);
-
- final PullRequest pullRequest = generatePullRequest(
- prNumber: 0,
- repoName: slug.name,
- labelName: 'revert of',
- body: 'Reverts flutter/flutter#1234',
- );
-
- final GithubPullRequestEvent githubPullRequestEvent = GithubPullRequestEvent(
- pullRequest: pullRequest,
- action: 'labeled',
- sender: User(login: 'ricardoamador'),
- );
-
- final Issue issue = Issue(
- id: 1234,
- assignee: User(login: 'keyonghan'),
- createdAt: DateTime.now(),
- );
-
- // setup fields
- githubService.githubIssueMock = issue;
- githubService.pullRequestMock = pullRequest;
- githubService.createCommentData = createCommentMock;
- validationService.approverService = FakeApproverService(config);
-
- // run test
- unawaited(pubsub.publish(config.pubsubRevertRequestSubscription, pullRequest));
- await validationService.processRevertOfRequest(
- result: queryResult,
- githubPullRequestEvent: githubPullRequestEvent,
- ackId: 'test',
- pubsub: pubsub,
- );
-
- // validate
- expect(githubService.issueComment, isNotNull);
- expect(githubService.labelRemoved, true);
- assert(pubsub.messagesQueue.isEmpty);
- });
-
- test('Validation failure, label is removed.', () async {
- // setup
- final FakeValidationFilter fakeValidationFilter = FakeValidationFilter();
- final FakeApproval fakeApproval = FakeApproval(config: config);
- fakeApproval.validationResult =
- ValidationResult(true, Action.REMOVE_LABEL, 'This PR has met approval requirements for merging.\n');
- final FakeRequiredCheckRuns fakeRequiredCheckRuns = FakeRequiredCheckRuns(config: config);
- fakeRequiredCheckRuns.validationResult =
- ValidationResult(true, Action.REMOVE_LABEL, 'All required check runs have completed.');
- final FakeMergeable fakeMergeable = FakeMergeable(config: config);
- fakeMergeable.validationResult = ValidationResult(
- false,
- Action.REMOVE_LABEL,
- 'Pull request flutter/flutter/1234 is not in a mergeable state.',
- );
- fakeValidationFilter.registerValidation(fakeApproval);
- fakeValidationFilter.registerValidation(fakeRequiredCheckRuns);
- fakeValidationFilter.registerValidation(fakeMergeable);
-
- final FakePubSub pubsub = FakePubSub();
-
- final PullRequestHelper flutterRequest = PullRequestHelper(
- prNumber: 0,
- lastCommitHash: oid,
- reviews: <PullRequestReviewHelper>[],
- );
-
- final auto.QueryResult queryResult = createQueryResult(flutterRequest);
-
- final PullRequest pullRequest = generatePullRequest(
- prNumber: 0,
- repoName: slug.name,
- labelName: 'revert of',
- body: 'Reverts flutter/flutter#1234',
- );
-
- final GithubPullRequestEvent githubPullRequestEvent = GithubPullRequestEvent(
- pullRequest: pullRequest,
- action: 'labeled',
- sender: User(login: 'ricardoamador'),
- );
-
- final Issue issue = Issue(
- id: 1234,
- assignee: User(login: 'keyonghan'),
- createdAt: DateTime.now(),
- );
-
- // setup fields
- githubService.githubIssueMock = issue;
- githubService.pullRequestMock = pullRequest;
- githubService.createCommentData = createCommentMock;
- validationService.approverService = FakeApproverService(config);
- validationService.validationFilter = fakeValidationFilter;
-
- // run test
- unawaited(pubsub.publish(config.pubsubRevertRequestSubscription, pullRequest));
- await validationService.processRevertOfRequest(
- result: queryResult,
- githubPullRequestEvent: githubPullRequestEvent,
- ackId: 'test',
- pubsub: pubsub,
- );
-
- // validate
- expect(githubService.issueComment, isNotNull);
- expect(githubService.labelRemoved, true);
- assert(pubsub.messagesQueue.isEmpty);
- });
-
- test('Temporary validation failure label not removed.', () async {
- // setup
- final FakeValidationFilter fakeValidationFilter = FakeValidationFilter();
- final FakeApproval fakeApproval = FakeApproval(config: config);
- fakeApproval.validationResult =
- ValidationResult(true, Action.REMOVE_LABEL, 'This PR has met approval requirements for merging.\n');
- final FakeRequiredCheckRuns fakeRequiredCheckRuns = FakeRequiredCheckRuns(config: config);
- fakeRequiredCheckRuns.validationResult =
- ValidationResult(false, Action.IGNORE_TEMPORARILY, 'All required check runs have not yet completed.');
- final FakeMergeable fakeMergeable = FakeMergeable(config: config);
- fakeMergeable.validationResult =
- ValidationResult(true, Action.REMOVE_LABEL, 'Pull request flutter/flutter/1234 is in a mergeable state.');
- fakeValidationFilter.registerValidation(fakeApproval);
- fakeValidationFilter.registerValidation(fakeRequiredCheckRuns);
- fakeValidationFilter.registerValidation(fakeMergeable);
-
- final FakePubSub pubsub = FakePubSub();
-
- final PullRequestHelper flutterRequest = PullRequestHelper(
- prNumber: 0,
- lastCommitHash: oid,
- reviews: <PullRequestReviewHelper>[],
- );
-
- final auto.QueryResult queryResult = createQueryResult(flutterRequest);
-
- final PullRequest pullRequest = generatePullRequest(
- prNumber: 0,
- repoName: slug.name,
- labelName: 'revert of',
- body: 'Reverts flutter/flutter#1234',
- );
-
- final GithubPullRequestEvent githubPullRequestEvent = GithubPullRequestEvent(
- pullRequest: pullRequest,
- action: 'labeled',
- sender: User(login: 'ricardoamador'),
- );
-
- final Issue issue = Issue(
- id: 1234,
- assignee: User(login: 'keyonghan'),
- createdAt: DateTime.now(),
- );
-
- // setup fields
- githubService.githubIssueMock = issue;
- githubService.pullRequestMock = pullRequest;
- githubService.createCommentData = createCommentMock;
- validationService.approverService = FakeApproverService(config);
- validationService.validationFilter = fakeValidationFilter;
-
- // run test
- unawaited(pubsub.publish(config.pubsubRevertRequestSubscription, pullRequest));
- await validationService.processRevertOfRequest(
- result: queryResult,
- githubPullRequestEvent: githubPullRequestEvent,
- ackId: 'test',
- pubsub: pubsub,
- );
-
- // validate
- expect(githubService.issueComment, isNull);
- expect(githubService.labelRemoved, false);
- assert(pubsub.messagesQueue.isNotEmpty);
- });
-
- test('Temp and hard failure result in label removed', () async {
- // setup
- final FakeValidationFilter fakeValidationFilter = FakeValidationFilter();
- final FakeApproval fakeApproval = FakeApproval(config: config);
- fakeApproval.validationResult =
- ValidationResult(true, Action.REMOVE_LABEL, 'This PR has met approval requirements for merging.\n');
- final FakeRequiredCheckRuns fakeRequiredCheckRuns = FakeRequiredCheckRuns(config: config);
- fakeRequiredCheckRuns.validationResult =
- ValidationResult(false, Action.IGNORE_TEMPORARILY, 'All required check runs have not yet completed.');
- final FakeMergeable fakeMergeable = FakeMergeable(config: config);
- fakeMergeable.validationResult = ValidationResult(
- false,
- Action.REMOVE_LABEL,
- 'Pull request flutter/flutter/1234 is not in a mergeable state.',
- );
- fakeValidationFilter.registerValidation(fakeApproval);
- fakeValidationFilter.registerValidation(fakeRequiredCheckRuns);
- fakeValidationFilter.registerValidation(fakeMergeable);
-
- final FakePubSub pubsub = FakePubSub();
-
- final PullRequestHelper flutterRequest = PullRequestHelper(
- prNumber: 0,
- lastCommitHash: oid,
- reviews: <PullRequestReviewHelper>[],
- );
-
- final auto.QueryResult queryResult = createQueryResult(flutterRequest);
-
- final PullRequest pullRequest = generatePullRequest(
- prNumber: 0,
- repoName: slug.name,
- labelName: 'revert of',
- body: 'Reverts flutter/flutter#1234',
- );
-
- final GithubPullRequestEvent githubPullRequestEvent = GithubPullRequestEvent(
- pullRequest: pullRequest,
- action: 'labeled',
- sender: User(login: 'ricardoamador'),
- );
-
- final Issue issue = Issue(
- id: 1234,
- assignee: User(login: 'keyonghan'),
- createdAt: DateTime.now(),
- );
-
- // setup fields
- githubService.githubIssueMock = issue;
- githubService.pullRequestMock = pullRequest;
- githubService.createCommentData = createCommentMock;
- validationService.approverService = FakeApproverService(config);
- validationService.validationFilter = fakeValidationFilter;
-
- // run test
- unawaited(pubsub.publish(config.pubsubRevertRequestSubscription, pullRequest));
- await validationService.processRevertOfRequest(
- result: queryResult,
- githubPullRequestEvent: githubPullRequestEvent,
- ackId: 'test',
- pubsub: pubsub,
- );
-
- // validate
- expect(githubService.issueComment, isNotNull);
- expect(githubService.labelRemoved, true);
- assert(pubsub.messagesQueue.isEmpty);
- });
-
- test('Merge valid "revert of" request', () async {
- // setup
- final FakeValidationFilter fakeValidationFilter = FakeValidationFilter();
- final FakeApproval fakeApproval = FakeApproval(config: config);
- fakeApproval.validationResult = ValidationResult(
- true,
- Action.REMOVE_LABEL,
- 'This PR has met approval requirements for merging.\n',
- );
- final FakeRequiredCheckRuns fakeRequiredCheckRuns = FakeRequiredCheckRuns(config: config);
- fakeRequiredCheckRuns.validationResult = ValidationResult(
- true,
- Action.REMOVE_LABEL,
- 'All required check runs have completed.',
- );
- final FakeMergeable fakeMergeable = FakeMergeable(config: config);
- fakeMergeable.validationResult = ValidationResult(
- true,
- Action.REMOVE_LABEL,
- 'Pull request flutter/flutter/1234 is in a mergeable state.',
- );
- fakeValidationFilter.registerValidation(fakeApproval);
- fakeValidationFilter.registerValidation(fakeRequiredCheckRuns);
- fakeValidationFilter.registerValidation(fakeMergeable);
-
- final FakePubSub pubsub = FakePubSub();
-
- final PullRequestHelper flutterRequest = PullRequestHelper(
- prNumber: 0,
- lastCommitHash: oid,
- reviews: <PullRequestReviewHelper>[],
- );
-
- final auto.QueryResult queryResult = createQueryResult(flutterRequest);
-
- final PullRequest pullRequest = generatePullRequest(
- prNumber: 0,
- repoName: slug.name,
- labelName: 'revert of',
- body: 'Reverts flutter/flutter#1234',
- );
-
- final GithubPullRequestEvent githubPullRequestEvent = GithubPullRequestEvent(
- pullRequest: pullRequest,
- action: 'labeled',
- sender: User(login: 'auto-submit[bot]'),
- );
-
- final Issue issue = Issue(
- id: 1234,
- assignee: User(login: 'keyonghan'),
- createdAt: DateTime.now(),
- );
-
- // setup fields
- githubService.githubIssueMock = issue;
- githubService.pullRequestMock = pullRequest;
- githubService.createCommentData = createCommentMock;
- githubService.mergeRequestMock = PullRequestMerge(
- merged: true,
- sha: 'sha',
- message: 'Pull Request successfully merged',
- );
- validationService.approverService = FakeApproverService(config);
- validationService.validationFilter = fakeValidationFilter;
-
- // run test
- unawaited(pubsub.publish(config.pubsubRevertRequestSubscription, pullRequest));
- await validationService.processRevertOfRequest(
- result: queryResult,
- githubPullRequestEvent: githubPullRequestEvent,
- ackId: 'test',
- pubsub: pubsub,
- );
-
- // validate
- expect(githubService.issueComment, isNull);
- expect(githubService.labelRemoved, false);
- assert(pubsub.messagesQueue.isEmpty);
- });
-
- test('Unable to merge valid "revert of" request.', () async {
- // setup
- final FakeValidationFilter fakeValidationFilter = FakeValidationFilter();
- final FakeApproval fakeApproval = FakeApproval(config: config);
- fakeApproval.validationResult = ValidationResult(
- true,
- Action.REMOVE_LABEL,
- 'This PR has met approval requirements for merging.\n',
- );
- final FakeRequiredCheckRuns fakeRequiredCheckRuns = FakeRequiredCheckRuns(config: config);
- fakeRequiredCheckRuns.validationResult = ValidationResult(
- true,
- Action.REMOVE_LABEL,
- 'All required check runs have completed.',
- );
- final FakeMergeable fakeMergeable = FakeMergeable(config: config);
- fakeMergeable.validationResult = ValidationResult(
- true,
- Action.REMOVE_LABEL,
- 'Pull request flutter/flutter/1234 is in a mergeable state.',
- );
- fakeValidationFilter.registerValidation(fakeApproval);
- fakeValidationFilter.registerValidation(fakeRequiredCheckRuns);
- fakeValidationFilter.registerValidation(fakeMergeable);
-
- final FakePubSub pubsub = FakePubSub();
-
- final PullRequestHelper flutterRequest = PullRequestHelper(
- prNumber: 0,
- lastCommitHash: oid,
- reviews: <PullRequestReviewHelper>[],
- );
-
- final auto.QueryResult queryResult = createQueryResult(flutterRequest);
-
- final PullRequest pullRequest = generatePullRequest(
- prNumber: 0,
- repoName: slug.name,
- labelName: 'revert of',
- body: 'Reverts flutter/flutter#1234',
- );
-
- final GithubPullRequestEvent githubPullRequestEvent = GithubPullRequestEvent(
- pullRequest: pullRequest,
- action: 'labeled',
- sender: User(login: 'ricardoamador'),
- );
-
- final Issue issue = Issue(
- id: 1234,
- assignee: User(login: 'keyonghan'),
- createdAt: DateTime.now(),
- );
-
- // setup fields
- githubService.githubIssueMock = issue;
- githubService.pullRequestMock = pullRequest;
- githubService.createCommentData = createCommentMock;
- githubService.mergeRequestMock = PullRequestMerge(
- merged: false,
- sha: 'sha',
- message: 'Pull Request was not merged successfully',
- );
- validationService.approverService = FakeApproverService(config);
- validationService.validationFilter = fakeValidationFilter;
-
- // run test
- unawaited(pubsub.publish(config.pubsubRevertRequestSubscription, pullRequest));
- await validationService.processRevertOfRequest(
- result: queryResult,
- githubPullRequestEvent: githubPullRequestEvent,
- ackId: 'test',
- pubsub: pubsub,
- );
-
- // validate
- expect(githubService.issueComment, isNotNull);
- expect(githubService.labelRemoved, true);
- assert(pubsub.messagesQueue.isEmpty);
- });
-
- test('Exhaust retries on merge on retryable error. Unable to merge.', () async {
- // setup
- validationService = RevertRequestValidationService(
- config,
- retryOptions: const RetryOptions(
- delayFactor: Duration.zero,
- maxDelay: Duration.zero,
- // three attempts
- maxAttempts: 3,
- ),
- );
-
- final FakeValidationFilter fakeValidationFilter = FakeValidationFilter();
- final FakeApproval fakeApproval = FakeApproval(config: config);
- fakeApproval.validationResult = ValidationResult(
- true,
- Action.REMOVE_LABEL,
- 'This PR has met approval requirements for merging.\n',
- );
- final FakeRequiredCheckRuns fakeRequiredCheckRuns = FakeRequiredCheckRuns(config: config);
- fakeRequiredCheckRuns.validationResult = ValidationResult(
- true,
- Action.REMOVE_LABEL,
- 'All required check runs have completed.',
- );
- final FakeMergeable fakeMergeable = FakeMergeable(config: config);
- fakeMergeable.validationResult = ValidationResult(
- true,
- Action.REMOVE_LABEL,
- 'Pull request flutter/flutter/1234 is in a mergeable state.',
- );
- fakeValidationFilter.registerValidation(fakeApproval);
- fakeValidationFilter.registerValidation(fakeRequiredCheckRuns);
- fakeValidationFilter.registerValidation(fakeMergeable);
-
- final FakePubSub pubsub = FakePubSub();
-
- final PullRequestHelper flutterRequest = PullRequestHelper(
- prNumber: 0,
- lastCommitHash: oid,
- reviews: <PullRequestReviewHelper>[],
- );
-
- final auto.QueryResult queryResult = createQueryResult(flutterRequest);
-
- final PullRequest pullRequest = generatePullRequest(
- prNumber: 0,
- repoName: slug.name,
- labelName: 'revert of',
- body: 'Reverts flutter/flutter#1234',
- );
-
- final GithubPullRequestEvent githubPullRequestEvent = GithubPullRequestEvent(
- pullRequest: pullRequest,
- action: 'labeled',
- sender: User(login: 'ricardoamador'),
- );
-
- final Issue issue = Issue(
- id: 1234,
- assignee: User(login: 'keyonghan'),
- createdAt: DateTime.now(),
- );
-
- // setup fields
- githubService.githubIssueMock = issue;
- githubService.pullRequestMock = pullRequest;
- githubService.createCommentData = createCommentMock;
-
- githubService.mergeRequestMock = PullRequestMerge(
- merged: false,
- sha: 'sha',
- message: 'Pull Request was not merged successfully',
- );
-
- validationService.approverService = FakeApproverService(config);
- validationService.validationFilter = fakeValidationFilter;
-
- // run test
- unawaited(pubsub.publish(config.pubsubRevertRequestSubscription, pullRequest));
- await validationService.processRevertOfRequest(
- result: queryResult,
- githubPullRequestEvent: githubPullRequestEvent,
- ackId: 'test',
- pubsub: pubsub,
- );
-
- // validate
- expect(githubService.issueComment, isNotNull);
- expect(githubService.labelRemoved, true);
- assert(pubsub.messagesQueue.isEmpty);
- });
-
- test('Merge fails first time then succeeds after retry.', () async {
- // setup
- validationService = RevertRequestValidationService(
- config,
- retryOptions: const RetryOptions(
- delayFactor: Duration.zero,
- maxDelay: Duration.zero,
- // three attempts
- maxAttempts: 3,
- ),
- );
-
- final FakeValidationFilter fakeValidationFilter = FakeValidationFilter();
- final FakeApproval fakeApproval = FakeApproval(config: config);
- fakeApproval.validationResult = ValidationResult(
- true,
- Action.REMOVE_LABEL,
- 'This PR has met approval requirements for merging.\n',
- );
- final FakeRequiredCheckRuns fakeRequiredCheckRuns = FakeRequiredCheckRuns(config: config);
- fakeRequiredCheckRuns.validationResult = ValidationResult(
- true,
- Action.REMOVE_LABEL,
- 'All required check runs have completed.',
- );
- final FakeMergeable fakeMergeable = FakeMergeable(config: config);
- fakeMergeable.validationResult = ValidationResult(
- true,
- Action.REMOVE_LABEL,
- 'Pull request flutter/flutter/1234 is in a mergeable state.',
- );
- fakeValidationFilter.registerValidation(fakeApproval);
- fakeValidationFilter.registerValidation(fakeRequiredCheckRuns);
- fakeValidationFilter.registerValidation(fakeMergeable);
-
- final FakePubSub pubsub = FakePubSub();
-
- final PullRequestHelper flutterRequest = PullRequestHelper(
- prNumber: 0,
- lastCommitHash: oid,
- reviews: <PullRequestReviewHelper>[],
- );
-
- final auto.QueryResult queryResult = createQueryResult(flutterRequest);
-
- final PullRequest pullRequest = generatePullRequest(
- prNumber: 0,
- repoName: slug.name,
- labelName: 'revert of',
- body: 'Reverts flutter/flutter#1234',
- );
-
- final GithubPullRequestEvent githubPullRequestEvent = GithubPullRequestEvent(
- pullRequest: pullRequest,
- action: 'labeled',
- sender: User(login: 'ricardoamador'),
- );
-
- final Issue issue = Issue(
- id: 1234,
- assignee: User(login: 'keyonghan'),
- createdAt: DateTime.now(),
- );
-
- // setup fields
- githubService.githubIssueMock = issue;
- githubService.pullRequestMock = pullRequest;
- githubService.createCommentData = createCommentMock;
-
- // TODO use the mock list.
- githubService.useMergeRequestMockList = true;
- final PullRequestMerge pullRequestMergeFail = PullRequestMerge(
- merged: false,
- sha: 'sha',
- message: 'Pull request was not merged successfully',
- );
- final PullRequestMerge pullRequestMergeSuccess = PullRequestMerge(
- merged: true,
- sha: 'sha',
- message: 'Pull request was merged successfully',
- );
-
- githubService.pullRequestMergeMockList = [pullRequestMergeFail, pullRequestMergeSuccess];
-
- validationService.approverService = FakeApproverService(config);
- validationService.validationFilter = fakeValidationFilter;
-
- // run test
- unawaited(pubsub.publish(config.pubsubRevertRequestSubscription, pullRequest));
- await validationService.processRevertOfRequest(
- result: queryResult,
- githubPullRequestEvent: githubPullRequestEvent,
- ackId: 'test',
- pubsub: pubsub,
- );
-
- // validate
- expect(githubService.issueComment, isNull);
- expect(githubService.labelRemoved, false);
- assert(pubsub.messagesQueue.isEmpty);
- });
-
- test('Merge is not retried on non retryable exception', () async {
- // setup
- validationService = RevertRequestValidationService(
- config,
- retryOptions: const RetryOptions(
- delayFactor: Duration.zero,
- maxDelay: Duration.zero,
- // three attempts
- maxAttempts: 3,
- ),
- );
-
- final FakeValidationFilter fakeValidationFilter = FakeValidationFilter();
- final FakeApproval fakeApproval = FakeApproval(config: config);
- fakeApproval.validationResult = ValidationResult(
- true,
- Action.REMOVE_LABEL,
- 'This PR has met approval requirements for merging.\n',
- );
- final FakeRequiredCheckRuns fakeRequiredCheckRuns = FakeRequiredCheckRuns(config: config);
- fakeRequiredCheckRuns.validationResult = ValidationResult(
- true,
- Action.REMOVE_LABEL,
- 'All required check runs have completed.',
- );
- final FakeMergeable fakeMergeable = FakeMergeable(config: config);
- fakeMergeable.validationResult = ValidationResult(
- true,
- Action.REMOVE_LABEL,
- 'Pull request flutter/flutter/1234 is in a mergeable state.',
- );
- fakeValidationFilter.registerValidation(fakeApproval);
- fakeValidationFilter.registerValidation(fakeRequiredCheckRuns);
- fakeValidationFilter.registerValidation(fakeMergeable);
-
- final FakePubSub pubsub = FakePubSub();
-
- final PullRequestHelper flutterRequest = PullRequestHelper(
- prNumber: 0,
- lastCommitHash: oid,
- reviews: <PullRequestReviewHelper>[],
- );
-
- final auto.QueryResult queryResult = createQueryResult(flutterRequest);
-
- final PullRequest pullRequest = generatePullRequest(
- prNumber: 0,
- repoName: slug.name,
- labelName: 'revert of',
- body: 'Reverts flutter/flutter#1234',
- );
-
- final GithubPullRequestEvent githubPullRequestEvent = GithubPullRequestEvent(
- pullRequest: pullRequest,
- action: 'labeled',
- sender: User(login: 'ricardoamador'),
- );
-
- final Issue issue = Issue(
- id: 1234,
- assignee: User(login: 'keyonghan'),
- createdAt: DateTime.now(),
- );
-
- // setup fields
- githubService.githubIssueMock = issue;
- githubService.pullRequestMock = pullRequest;
- githubService.createCommentData = createCommentMock;
-
- githubService.throwExceptionOnMerge = true;
- githubService.mergeRequestMock = PullRequestMerge(
- merged: false,
- sha: 'sha',
- message: 'Pull Request was not merged successfully',
- );
-
- validationService.approverService = FakeApproverService(config);
- validationService.validationFilter = fakeValidationFilter;
-
- // run test
- unawaited(pubsub.publish(config.pubsubRevertRequestSubscription, pullRequest));
- await validationService.processRevertOfRequest(
- result: queryResult,
- githubPullRequestEvent: githubPullRequestEvent,
- ackId: 'test',
- pubsub: pubsub,
- );
-
- // validate
- expect(githubService.issueComment, isNotNull);
- expect(githubService.labelRemoved, true);
- assert(pubsub.messagesQueue.isEmpty);
- });
- });
-
- group('processMerge:', () {
- test('Correct PR titles when merging to use Reland', () async {
- final PullRequest pullRequest = generatePullRequest(
- prNumber: 0,
- repoName: slug.name,
- title: 'Revert "Revert "My first PR!"',
- mergeable: true,
- );
- githubService.pullRequestData = pullRequest;
- githubService.mergeRequestMock = PullRequestMerge(
- merged: true,
- sha: pullRequest.mergeCommitSha,
- );
-
- final MergeResult result = await validationService.processMerge(
- config: config,
- messagePullRequest: pullRequest,
- );
-
- expect(result.message, contains('Reland "My first PR!"'));
- });
-
- test('includes PR description in commit message', () async {
- final PullRequest pullRequest = generatePullRequest(
- prNumber: 0,
- repoName: slug.name,
- title: 'PR title',
- // The test-only helper function `generatePullRequest` will interpolate
- // this string into a JSON string which will then be decoded--thus, this string must be
- // a valid JSON substring, with escaped newlines.
- body: r'PR description\nwhich\nis multiline.',
- mergeable: true,
- );
- githubService.pullRequestData = pullRequest;
- githubService.mergeRequestMock = PullRequestMerge(
- merged: true,
- sha: pullRequest.mergeCommitSha,
- );
- final MergeResult result = await validationService.processMerge(
- config: config,
- messagePullRequest: pullRequest,
- );
-
- expect(result.message, '''
-PR description
-which
-is multiline.''');
- });
-
- test('commit message filters out markdown checkboxes', () async {
- const String prTitle = 'Important update #4';
- const String prBody = '''
-Various bugfixes and performance improvements.
-
-Fixes #12345 and #3.
-This is the second line in a paragraph.
-
-## Pre-launch Checklist
-
-- [ ] I read the [Contributor Guide] and followed the process outlined there for submitting PRs.
-- [ ] I read the [Tree Hygiene] wiki page, which explains my responsibilities.
-- [ ] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement].
-- [x] I signed the [CLA].
-- [ ] I listed at least one issue that this PR fixes in the description above.
-- [ ] I updated/added relevant documentation (doc comments with `///`).
-- [X] I added new tests to check the change I am making, or this PR is [test-exempt].
-- [ ] All existing and new tests are passing.
-
-If you need help, consider asking for advice on the #hackers-new channel on [Discord].
-
-<!-- Links -->
-[Contributor Guide]: https://github.com/flutter/flutter/wiki/Tree-hygiene#overview
-[Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene
-[test-exempt]: https://github.com/flutter/flutter/wiki/Tree-hygiene#tests
-[Flutter Style Guide]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo
-[Features we expect every widget to implement]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#features-we-expect-every-widget-to-implement
-[CLA]: https://cla.developers.google.com/
-[flutter/tests]: https://github.com/flutter/tests
-[breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes
-[Discord]: https://github.com/flutter/flutter/wiki/Chat''';
-
- final PullRequest pullRequest = generatePullRequest(
- prNumber: 0,
- repoName: slug.name,
- title: prTitle,
- // The test-only helper function `generatePullRequest` will interpolate
- // this string into a JSON string which will then be decoded--thus, this string must be
- // a valid JSON substring, with escaped newlines.
- body: prBody.replaceAll('\n', r'\n'),
- mergeable: true,
- );
- githubService.pullRequestData = pullRequest;
- githubService.mergeRequestMock = PullRequestMerge(
- merged: true,
- sha: pullRequest.mergeCommitSha,
- );
-
- final MergeResult result = await validationService.processMerge(
- config: config,
- messagePullRequest: pullRequest,
- );
-
- expect(result.result, isTrue);
- expect(result.message, '''
-Various bugfixes and performance improvements.
-
-Fixes #12345 and #3.
-This is the second line in a paragraph.''');
- });
- });
-}
diff --git a/auto_submit/test/src/action/fake_revert_method.dart b/auto_submit/test/src/action/fake_revert_method.dart
deleted file mode 100644
index 0e7eea0..0000000
--- a/auto_submit/test/src/action/fake_revert_method.dart
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2023 The Flutter 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 'package:auto_submit/action/revert_method.dart';
-import 'package:auto_submit/service/config.dart';
-import 'package:github/src/common/model/pulls.dart';
-
-class FakeRevertMethod implements RevertMethod {
- Object? object;
- bool throwException = false;
-
- @override
- Future<Object?> createRevert(Config config, String initiatingAuthor, PullRequest pullRequest) async {
- if (throwException) {
- throw 'Crappy github exception not related to the actual error.';
- }
- return object;
- }
-}
diff --git a/auto_submit/test/src/configuration/fake_repository_configuration_manager.dart b/auto_submit/test/src/configuration/fake_repository_configuration_manager.dart
deleted file mode 100644
index b7fef72..0000000
--- a/auto_submit/test/src/configuration/fake_repository_configuration_manager.dart
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2023 The Flutter 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 'package:auto_submit/configuration/repository_configuration.dart';
-import 'package:auto_submit/configuration/repository_configuration_manager.dart';
-import 'package:auto_submit/service/config.dart';
-import 'package:github/src/common/model/repos.dart';
-import 'package:neat_cache/neat_cache.dart';
-
-class FakeRepositoryConfigurationManager implements RepositoryConfigurationManager {
- FakeRepositoryConfigurationManager(this.config, this.cache);
-
- String? yamlConfig;
-
- @override
- final Cache cache;
-
- @override
- final Config config;
-
- late RepositoryConfiguration? repositoryConfigurationMock;
-
- late RepositoryConfiguration? mergedRepositoryConfigurationMock;
-
- @override
- Future<RepositoryConfiguration> readRepositoryConfiguration(RepositorySlug slug) async {
- return repositoryConfigurationMock!;
- }
-
- @override
- RepositoryConfiguration mergeConfigurations(
- RepositoryConfiguration globalConfiguration,
- RepositoryConfiguration localConfiguration,
- ) {
- return mergedRepositoryConfigurationMock!;
- }
-}
diff --git a/auto_submit/test/src/request_handling/fake_authentication.dart b/auto_submit/test/src/request_handling/fake_authentication.dart
deleted file mode 100644
index ab2159b..0000000
--- a/auto_submit/test/src/request_handling/fake_authentication.dart
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:auto_submit/request_handling/authentication.dart';
-import 'package:auto_submit/requests/exceptions.dart';
-import 'package:shelf/shelf.dart';
-
-// ignore: must_be_immutable
-class FakeCronAuthProvider implements CronAuthProvider {
- FakeCronAuthProvider({
- this.authenticated = true,
- });
-
- bool authenticated;
-
- @override
- Future<bool> authenticate(Request request) async {
- if (authenticated) {
- return true;
- } else {
- throw const Unauthenticated('Not authenticated');
- }
- }
-}
diff --git a/auto_submit/test/src/request_handling/fake_pubsub.dart b/auto_submit/test/src/request_handling/fake_pubsub.dart
deleted file mode 100644
index 75d5a4c..0000000
--- a/auto_submit/test/src/request_handling/fake_pubsub.dart
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2019 The Flutter 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:convert';
-import 'dart:math';
-
-import 'package:auto_submit/request_handling/pubsub.dart';
-import 'package:googleapis/pubsub/v1.dart';
-
-class FakePubSub extends PubSub {
- List<dynamic> messagesQueue = <dynamic>[];
- // The iteration of `pull` API calls.
- int iteration = -1;
- // Number of messages in each Pub/Sub pull call. This mocks the API
- // returning random number of messages each time.
- int messageSize = 2;
-
- @override
- Future<void> publish(String topicName, dynamic json) async {
- final String messageData = jsonEncode(json);
- final List<int> messageBytes = utf8.encode(messageData);
- final String messageBase64 = base64Encode(messageBytes);
- messagesQueue.add(messageBase64);
- }
-
- @override
- Future<PullResponse> pull(String subscription, int maxMessages) async {
- // The list will be empty if there are no more messages available in the backlog.
- final List<ReceivedMessage> receivedMessages = <ReceivedMessage>[];
- iteration++;
- if (messagesQueue.isNotEmpty) {
- int i = iteration * messageSize;
- // Returns only allowed max number of messages. The number should not be greater than
- // `maxMessages`, the available messages, and the number allowed in each call. The
- // last number is to mock real `pull` API call.
- while (i < min(min(maxMessages, messagesQueue.length), (iteration + 1) * messageSize)) {
- receivedMessages.add(
- ReceivedMessage(
- message: PubsubMessage(data: messagesQueue[i] as String, messageId: '$i'),
- ackId: 'ackId_$i',
- ),
- );
- i++;
- }
- return PullResponse(receivedMessages: receivedMessages);
- }
- return PullResponse(receivedMessages: receivedMessages);
- }
-
- @override
- Future<void> acknowledge(String subscription, String ackId) async {
- if (messagesQueue.isNotEmpty) {
- messagesQueue.removeAt(messagesQueue.length - 1);
- }
- }
-}
diff --git a/auto_submit/test/src/service/fake_approver_service.dart b/auto_submit/test/src/service/fake_approver_service.dart
deleted file mode 100644
index 9acd918..0000000
--- a/auto_submit/test/src/service/fake_approver_service.dart
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2022 The Flutter 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 'package:auto_submit/service/approver_service.dart';
-import 'package:github/github.dart' as gh;
-
-class FakeApproverService extends ApproverService {
- FakeApproverService(super.config);
-
- @override
- Future<void> autoApproval(gh.PullRequest pullRequest) async {
- // no op
- }
-}
diff --git a/auto_submit/test/src/service/fake_bigquery_service.dart b/auto_submit/test/src/service/fake_bigquery_service.dart
deleted file mode 100644
index d9cbd3a..0000000
--- a/auto_submit/test/src/service/fake_bigquery_service.dart
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2022 The Flutter 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 'package:auto_submit/service/bigquery.dart';
-import 'package:googleapis/bigquery/v2.dart';
-
-import '../../utilities/mocks.mocks.dart';
-
-class FakeBigqueryService extends BigqueryService {
- FakeBigqueryService(this.jobsResource) : super(MockAccessClientProvider());
-
- JobsResource jobsResource;
-
- @override
- Future<JobsResource> defaultJobs() async {
- return jobsResource;
- }
-}
diff --git a/auto_submit/test/src/service/fake_config.dart b/auto_submit/test/src/service/fake_config.dart
deleted file mode 100644
index d0454a9..0000000
--- a/auto_submit/test/src/service/fake_config.dart
+++ /dev/null
@@ -1,96 +0,0 @@
-// Copyright 2022 The Flutter 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:async';
-
-import 'package:auto_submit/configuration/repository_configuration.dart';
-import 'package:auto_submit/service/bigquery.dart';
-import 'package:auto_submit/service/config.dart';
-import 'package:auto_submit/service/github_service.dart';
-import 'package:auto_submit/service/secrets.dart';
-import 'package:github/github.dart';
-import 'package:neat_cache/neat_cache.dart';
-import 'package:graphql/client.dart';
-
-import 'fake_github_service.dart';
-
-// Represents a fake config to be used in unit test.
-class FakeConfig extends Config {
- FakeConfig({
- this.githubClient,
- this.githubGraphQLClient,
- this.githubService,
- this.rollerAccountsValue,
- this.overrideTreeStatusLabelValue,
- this.webhookKey,
- this.kPubsubPullNumberValue,
- this.bigqueryService,
- }) : super(
- cacheProvider: Cache.inMemoryCacheProvider(4),
- secretManager: LocalSecretManager(),
- );
-
- GitHub? githubClient;
- GraphQLClient? githubGraphQLClient;
- GithubService? githubService = FakeGithubService();
- Set<String>? rollerAccountsValue;
- String? overrideTreeStatusLabelValue;
- String? webhookKey;
- int? kPubsubPullNumberValue;
- BigqueryService? bigqueryService;
- RepositoryConfiguration? repositoryConfigurationMock;
-
- @override
- String get pubsubPullRequestTopic => 'auto-submit-queue';
- @override
- String get pubsubPullRequestSubscription => 'auto-submit-queue-sub';
- @override
- String get pubsubRevertRequestTopic => 'auto-submit-revert-queue';
- @override
- String get pubsubRevertRequestSubscription => 'auto-submit-revert-queue-sub';
-
- @override
- int get kPubsubPullNumber => kPubsubPullNumberValue ?? 1;
-
- @override
- Future<GitHub> createGithubClient(RepositorySlug slug) async => githubClient!;
-
- @override
- Future<GitHub> createFlutterGitHubBotClient(RepositorySlug slug) async => githubClient!;
-
- @override
- Future<GithubService> createGithubService(RepositorySlug slug) async => githubService ?? FakeGithubService();
-
- @override
- Future<GraphQLClient> createGitHubGraphQLClient(RepositorySlug slug) async => githubGraphQLClient!;
-
- @override
- Set<String> get rollerAccounts =>
- rollerAccountsValue ??
- const <String>{
- 'skia-flutter-autoroll',
- 'engine-flutter-autoroll',
- 'dependabot[bot]',
- 'dependabot',
- };
-
- @override
- String get overrideTreeStatusLabel => overrideTreeStatusLabelValue ?? 'warning: land on red to fix tree breakage';
-
- @override
- Future<String> getWebhookKey() async {
- return webhookKey ?? 'not_a_real_key';
- }
-
- @override
- Future<String> getFlutterGitHubBotToken() async {
- return 'not_a_real_token';
- }
-
- @override
- Future<BigqueryService> createBigQueryService() async => bigqueryService!;
-
- @override
- Future<RepositoryConfiguration> getRepositoryConfiguration(RepositorySlug slug) async => repositoryConfigurationMock!;
-}
diff --git a/auto_submit/test/src/service/fake_github_service.dart b/auto_submit/test/src/service/fake_github_service.dart
deleted file mode 100644
index 00b0f0d..0000000
--- a/auto_submit/test/src/service/fake_github_service.dart
+++ /dev/null
@@ -1,390 +0,0 @@
-// Copyright 2022 The Flutter 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:convert';
-
-import 'package:auto_submit/service/github_service.dart';
-import 'package:github/github.dart';
-import 'package:shelf/src/response.dart';
-
-import '../../utilities/mocks.dart';
-
-/// A fake GithubService implementation.
-class FakeGithubService implements GithubService {
- FakeGithubService({
- MockGitHub? client,
- String? checkRunsMock,
- String? commitMock,
- String? pullRequest,
- String? compareTwoCommitsMock,
- String? successMergeMock,
- String? createCommentMock,
- String? pullRequestMergeMock,
- }) : github = client ?? MockGitHub();
-
- @override
- final MockGitHub github;
-
- String? checkRunsMock;
- String? commitMock;
- PullRequest? pullRequestMock;
- String? compareTwoCommitsMock;
- String? successMergeMock;
- String? createCommentMock;
- String? pullRequestMergeMock;
- String? pullRequestFilesJsonMock;
- Issue? githubIssueMock;
- String? githubFileContents;
-
- bool useMergeRequestMockList = false;
- bool trackMergeRequestCalls = false;
- PullRequestMerge? mergeRequestMock;
- List<PullRequestMerge> pullRequestMergeMockList = [];
-
- /// map to track pull request calls using pull number and repository slug.
- Map<int, RepositorySlug> verifyPullRequestMergeCallMap = {};
-
- bool throwOnCreateIssue = false;
-
- /// Setting either of these flags to true will pop the front element from the
- /// list. Setting either to false will just return the non list version from
- /// the appropriate method.
- bool usePullRequestList = false;
- bool usePullRequestFilesList = false;
-
- List<String?> pullRequestFilesMockList = [];
- List<PullRequest?> pullRequestMockList = [];
-
- IssueComment? issueComment;
- bool useRealComment = false;
- bool labelRemoved = false;
-
- bool compareReturnValue = false;
- bool skipRealCompare = false;
-
- set checkRunsData(String? checkRunsMock) {
- this.checkRunsMock = checkRunsMock;
- }
-
- set commitData(String? commitMock) {
- this.commitMock = commitMock;
- }
-
- set pullRequestData(PullRequest? pullRequestMock) {
- this.pullRequestMock = pullRequestMock;
- }
-
- set compareTwoCommitsData(String? compareTwoCommitsMock) {
- this.compareTwoCommitsMock = compareTwoCommitsMock;
- }
-
- set successMergeData(String? successMergeMock) {
- this.successMergeMock = successMergeMock;
- }
-
- set createCommentData(String? createCommentMock) {
- this.createCommentMock = createCommentMock;
- }
-
- set pullRequestMergeData(String? pullRequestMergeMock) {
- this.pullRequestMergeMock = pullRequestMergeMock;
- }
-
- set pullrequestFilesData(String? pullRequestFilesMock) {
- pullRequestFilesJsonMock = pullRequestFilesMock;
- }
-
- set githubIssue(Issue? issue) {
- githubIssueMock = issue;
- }
-
- @override
- Future<List<CheckRun>> getCheckRuns(
- RepositorySlug slug,
- String ref,
- ) async {
- final rawBody = json.decode(checkRunsMock!) as Map<String, dynamic>;
- final List<dynamic> checkRunsBody = rawBody['check_runs']! as List<dynamic>;
- final List<CheckRun> checkRuns = <CheckRun>[];
- if ((checkRunsBody[0] as Map<String, dynamic>).isNotEmpty) {
- checkRuns.addAll(
- checkRunsBody.map((dynamic checkRun) => CheckRun.fromJson(checkRun as Map<String, dynamic>)).toList(),
- );
- }
- return checkRuns;
- }
-
- @override
- Future<List<CheckRun>> getCheckRunsFiltered({
- required RepositorySlug slug,
- required String ref,
- String? checkName,
- CheckRunStatus? status,
- CheckRunFilter? filter,
- }) async {
- final List<CheckRun> checkRuns = await getCheckRuns(slug, ref);
- if (checkName != null) {
- final List<CheckRun> checkRunsFilteredByName = [];
- for (CheckRun checkRun in checkRuns) {
- if (checkRun.name == checkName) {
- checkRunsFilteredByName.add(checkRun);
- }
- }
- return checkRunsFilteredByName;
- }
- return checkRuns;
- }
-
- @override
- Future<RepositoryCommit> getCommit(RepositorySlug slug, String sha) async {
- final RepositoryCommit commit = RepositoryCommit.fromJson(jsonDecode(commitMock!) as Map<String, dynamic>);
- return commit;
- }
-
- @override
- Future<PullRequest> getPullRequest(RepositorySlug slug, int pullRequestNumber) async {
- PullRequest pullRequest;
- if (usePullRequestList && pullRequestMockList.isNotEmpty) {
- pullRequest = pullRequestMockList.removeAt(0)!;
- } else if (usePullRequestList && pullRequestMockList.isEmpty) {
- throw Exception('List is empty.');
- } else {
- pullRequest = pullRequestMock!;
- }
- return pullRequest;
- }
-
- @override
- Future<GitHubComparison> compareTwoCommits(RepositorySlug slug, String refBase, String refHead) async {
- final GitHubComparison githubComparison =
- GitHubComparison.fromJson(jsonDecode(compareTwoCommitsMock!) as Map<String, dynamic>);
- return githubComparison;
- }
-
- @override
- Future<bool> removeLabel(RepositorySlug slug, int issueNumber, String label) async {
- labelRemoved = true;
- return labelRemoved;
- }
-
- @override
- Future<List<IssueLabel>> addLabels(RepositorySlug slug, int issueNumber, List<String> labels) async {
- final List<IssueLabel> labelsAdded = [];
- for (String labelName in labels) {
- labelsAdded.add(IssueLabel(name: labelName));
- }
- return labelsAdded;
- }
-
- @override
- Future<IssueComment> createComment(RepositorySlug slug, int number, String commentBody) async {
- if (useRealComment) {
- issueComment = IssueComment(id: number, body: commentBody);
- } else {
- issueComment = IssueComment.fromJson(jsonDecode(createCommentMock!) as Map<String, dynamic>);
- }
- return issueComment!;
- }
-
- @override
- Future<bool> updateBranch(RepositorySlug slug, int number, String headSha) async {
- return true;
- }
-
- @override
- Future<Response> autoMergeBranch(PullRequest pullRequest) {
- // TODO: implement autoMergeBranch
- throw UnimplementedError();
- }
-
- @override
- Future<List<PullRequestFile>> getPullRequestFiles(RepositorySlug slug, PullRequest pullRequest) async {
- String pullRequestData;
-
- if (usePullRequestFilesList && pullRequestFilesMockList.isNotEmpty) {
- pullRequestData = pullRequestFilesMockList.removeAt(0)!;
- } else if (usePullRequestFilesList && pullRequestFilesMockList.isEmpty) {
- throw Exception('File list is empty.');
- } else {
- pullRequestData = pullRequestFilesJsonMock as String;
- }
-
- final List<PullRequestFile> pullRequestFileList = [];
-
- final dynamic parsedList = jsonDecode(pullRequestData);
-
- for (dynamic d in parsedList) {
- final PullRequestFile file = PullRequestFile.fromJson(d as Map<String, dynamic>);
- pullRequestFileList.add(file);
- }
-
- return pullRequestFileList;
- }
-
- @override
- Future<Issue> createIssue({
- required RepositorySlug slug,
- required String title,
- required String body,
- List<String>? labels,
- String? assignee,
- List<String>? assignees,
- String? state,
- }) async {
- if (throwOnCreateIssue) {
- throw GitHubError(github, 'Exception on github create issue.');
- }
- return githubIssueMock!;
- }
-
- @override
- Future<Issue> getIssue({required RepositorySlug slug, required int issueNumber}) async {
- return githubIssueMock!;
- }
-
- bool throwExceptionOnMerge = false;
-
- /// If useMergeRequestMockList is true then we will return elements from that
- /// list until it is empty.
- ///
- /// The developer should track the number of times this method is called as
- /// managing an empty list is not done here.
- @override
- Future<PullRequestMerge> mergePullRequest(
- RepositorySlug slug,
- int number, {
- String? commitMessage,
- MergeMethod? mergeMethod,
- String? requestSha,
- }) async {
- if (throwExceptionOnMerge) {
- throw Exception('Exception occurred during merging of pull request.');
- }
- verifyPullRequestMergeCallMap[number] = slug;
- if (useMergeRequestMockList) {
- return pullRequestMergeMockList.removeAt(0);
- } else {
- return mergeRequestMock!;
- }
- }
-
- void verifyMergePullRequests(Map<int, RepositorySlug> expected) {
- assert(verifyPullRequestMergeCallMap.length == expected.length);
- verifyPullRequestMergeCallMap.forEach((key, value) {
- assert(expected.containsKey(key));
- assert(expected[key] == value);
- });
- }
-
- bool throwExceptionFileContents = false;
-
- List<String> fileContentsMockList = [];
-
- @override
- Future<String> getFileContents(RepositorySlug slug, String path, {String? ref}) async {
- if (throwExceptionFileContents) {
- throw 'Contents do not point to a file.';
- }
-
- // Assume that the list is not empty.
- return fileContentsMockList.removeAt(0);
- }
-
- TeamMembershipState? teamMembershipStateMock = TeamMembershipState('active');
-
- String defaultBranch = 'main';
- bool throwOnDefaultBranch = false;
- Exception exception = Exception('Generic exception.');
-
- @override
- Future<String> getDefaultBranch(RepositorySlug slug) async {
- if (throwOnDefaultBranch) {
- throw exception;
- } else {
- return defaultBranch;
- }
- }
-
- Repository repositoryMock = Repository();
-
- @override
- Future<Repository> getRepository(RepositorySlug slug) async {
- return repositoryMock;
- }
-
- Map<String, bool> isTeamMemberMockMap = <String, bool>{};
-
- @override
- Future<bool> isTeamMember(String team, String user, String org) async {
- if (!isTeamMemberMockMap.containsKey(user)) {
- return false;
- }
- return isTeamMemberMockMap[user]!;
- }
-
- @override
- Future<PullRequest> createPullRequest({
- required RepositorySlug slug,
- String? title,
- String? head,
- required String base,
- bool draft = false,
- String? body,
- }) {
- // TODO: implement createPullRequest
- throw UnimplementedError();
- }
-
- @override
- Future<List<PullRequest>> listPullRequests(
- RepositorySlug slug, {
- int? pages,
- String? base,
- String direction = 'desc',
- String? head,
- String sort = 'created',
- String state = 'open',
- }) {
- // TODO: implement listPullRequests
- throw UnimplementedError();
- }
-
- String? branchMockData;
-
- set branchMock(String data) => branchMock = data;
-
- @override
- Future<Branch> getBranch(RepositorySlug slug, String branchName) async {
- return Branch.fromJson(json.decode(branchMockData!));
- }
-
- bool addReviewersToPullRequestMock = true;
-
- @override
- Future<bool> addReviewersToPullRequest(
- RepositorySlug slug,
- int pullRequestNumber,
- List<String> reviewerLogins,
- ) async {
- return addReviewersToPullRequestMock;
- }
-
- bool addAssigneeMock = true;
-
- @override
- Future<bool> addAssignee(
- RepositorySlug slug,
- int number,
- List<String> assignees,
- ) async {
- return addAssigneeMock;
- }
-
- bool deleteBranchMock = true;
-
- @override
- Future<bool> deleteBranch(RepositorySlug slug, String branchName) async {
- return deleteBranchMock;
- }
-}
diff --git a/auto_submit/test/src/service/fake_graphql_client.dart b/auto_submit/test/src/service/fake_graphql_client.dart
deleted file mode 100644
index 7dac634..0000000
--- a/auto_submit/test/src/service/fake_graphql_client.dart
+++ /dev/null
@@ -1,134 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:gql/ast.dart';
-import 'package:graphql/client.dart';
-import 'package:test/test.dart';
-
-class FakeGraphQLClient implements GraphQLClient {
- late QueryResult Function(MutationOptions) mutateResultForOptions;
- late QueryResult Function(QueryOptions) queryResultForOptions;
-
- @override
- late QueryManager queryManager;
-
- @override
- Link get link => throw UnimplementedError();
-
- final List<QueryOptions> queries = <QueryOptions>[];
- final List<MutationOptions> mutations = <MutationOptions>[];
-
- // This allows us to simulate returning QueryResults in an order.
- final List<QueryResult> mutationMap = <QueryResult>[];
- bool useMutationMapOnMutate = false;
-
- @override
- Future<QueryResult<T>> mutate<T>(MutationOptions options) async {
- mutations.add(options);
- if (useMutationMapOnMutate) {
- return mutationMap.removeAt(0) as QueryResult<T>;
- }
- return mutateResultForOptions(options) as QueryResult<T>;
- }
-
- @override
- Future<QueryResult<T>> query<T>(QueryOptions options) async {
- queries.add(options);
- return queryResultForOptions(options) as QueryResult<T>;
- }
-
- void verifyQueries(List<QueryOptions> expected) {
- expect(queries.length, expected.length);
- for (int i = 0; i < queries.length; i++) {
- expect(
- queries[i].properties,
- equals(expected[i].properties),
- );
- }
- }
-
- void verifyMutations(List<MutationOptions> expected) {
- expect(mutations.length, expected.length);
- for (int i = 0; i < mutations.length; i++) {
- expect(
- mutations[i].properties,
- equals(expected[i].properties),
- );
- }
- }
-
- @override
- late DefaultPolicies defaultPolicies;
-
- @override
- Map<String, dynamic> readFragment(FragmentRequest fragmentRequest, {bool? optimistic = true}) {
- throw UnimplementedError();
- }
-
- @override
- Map<String, dynamic> readQuery(Request request, {bool? optimistic = true}) {
- throw UnimplementedError();
- }
-
- @override
- Future<List<QueryResult>> resetStore({bool refetchQueries = true}) {
- throw UnimplementedError();
- }
-
- @override
- void writeFragment(FragmentRequest fragmentRequest, {bool? broadcast = true, Map<String, dynamic>? data}) {}
-
- @override
- void writeQuery(Request request, {Map<String, dynamic>? data, bool? broadcast = true}) {}
-
- @override
- GraphQLCache get cache => throw UnimplementedError();
-
- @override
- GraphQLClient copyWith({
- Link? link,
- GraphQLCache? cache,
- DefaultPolicies? defaultPolicies,
- bool? alwaysRebroadcast,
- }) {
- throw UnimplementedError();
- }
-
- @override
- Future<QueryResult<T>> fetchMore<T>(
- FetchMoreOptions fetchMoreOptions, {
- required QueryOptions<T> originalOptions,
- required QueryResult<T> previousResult,
- }) {
- throw UnimplementedError();
- }
-
- @override
- Stream<QueryResult<T>> subscribe<T>(SubscriptionOptions<T> options) {
- throw UnimplementedError();
- }
-
- @override
- ObservableQuery<T> watchMutation<T>(WatchQueryOptions<T> options) {
- throw UnimplementedError();
- }
-
- @override
- ObservableQuery<T> watchQuery<T>(WatchQueryOptions<T> options) {
- throw UnimplementedError();
- }
-}
-
-QueryResult createFakeQueryResult({
- Map<String, dynamic>? data,
- OperationException? exception,
-}) =>
- QueryResult(
- data: data,
- exception: exception,
- options: QueryOptions(
- document: const DocumentNode(),
- ),
- source: QueryResultSource.network,
- );
diff --git a/auto_submit/test/src/validations/fake_approval.dart b/auto_submit/test/src/validations/fake_approval.dart
deleted file mode 100644
index 370bdda..0000000
--- a/auto_submit/test/src/validations/fake_approval.dart
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2023 The Flutter 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 'package:auto_submit/model/auto_submit_query_result.dart';
-
-import 'package:auto_submit/validations/approval.dart';
-import 'package:auto_submit/validations/validation.dart';
-import 'package:github/github.dart' as github;
-
-class FakeApproval extends Approval {
- FakeApproval({required super.config});
-
- ValidationResult? validationResult;
-
- @override
- String get name => 'FakeApproval';
-
- /// Implements the code review approval logic.
- @override
- Future<ValidationResult> validate(QueryResult result, github.PullRequest messagePullRequest) async {
- return validationResult ?? ValidationResult(true, Action.REMOVE_LABEL, '');
- }
-}
diff --git a/auto_submit/test/src/validations/fake_mergeable.dart b/auto_submit/test/src/validations/fake_mergeable.dart
deleted file mode 100644
index 47fd8c4..0000000
--- a/auto_submit/test/src/validations/fake_mergeable.dart
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright 2023 The Flutter 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 'package:auto_submit/model/auto_submit_query_result.dart';
-import 'package:auto_submit/validations/mergeable.dart';
-import 'package:auto_submit/validations/validation.dart';
-import 'package:github/github.dart' as github;
-
-class FakeMergeable extends Mergeable {
- FakeMergeable({required super.config});
-
- ValidationResult? validationResult;
-
- @override
- String get name => 'FakeMergeable';
-
- @override
- Future<ValidationResult> validate(QueryResult result, github.PullRequest messagePullRequest) async {
- return validationResult ?? ValidationResult(true, Action.REMOVE_LABEL, '');
- }
-}
diff --git a/auto_submit/test/src/validations/fake_required_check_runs.dart b/auto_submit/test/src/validations/fake_required_check_runs.dart
deleted file mode 100644
index 9fbca58..0000000
--- a/auto_submit/test/src/validations/fake_required_check_runs.dart
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2023 The Flutter 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 'package:auto_submit/validations/required_check_runs.dart';
-import 'package:auto_submit/model/auto_submit_query_result.dart' as auto;
-
-import 'package:auto_submit/validations/validation.dart';
-import 'package:github/github.dart' as github;
-
-class FakeRequiredCheckRuns extends RequiredCheckRuns {
- FakeRequiredCheckRuns({required super.config});
-
- ValidationResult? validationResult;
-
- @override
- String get name => 'FakeRequiredCheckRuns';
-
- @override
- Future<ValidationResult> validate(auto.QueryResult result, github.PullRequest messagePullRequest) async {
- return validationResult ?? ValidationResult(true, Action.REMOVE_LABEL, '');
- }
-}
diff --git a/auto_submit/test/src/validations/fake_validation_filter.dart b/auto_submit/test/src/validations/fake_validation_filter.dart
deleted file mode 100644
index 9dcc203..0000000
--- a/auto_submit/test/src/validations/fake_validation_filter.dart
+++ /dev/null
@@ -1,19 +0,0 @@
-// Copyright 2023 The Flutter 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 'package:auto_submit/validations/validation.dart';
-import 'package:auto_submit/validations/validation_filter.dart';
-
-class FakeValidationFilter implements ValidationFilter {
- final Set<Validation> validations = {};
-
- void registerValidation(Validation newValidation) {
- validations.add(newValidation);
- }
-
- @override
- Set<Validation> getValidations() {
- return validations;
- }
-}
diff --git a/auto_submit/test/utilities/mocks.dart b/auto_submit/test/utilities/mocks.dart
deleted file mode 100644
index 06dafe0..0000000
--- a/auto_submit/test/utilities/mocks.dart
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2022 The Flutter 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 'package:auto_submit/service/access_client_provider.dart';
-import 'package:auto_submit/service/approver_service.dart';
-import 'package:github/github.dart';
-import 'package:googleapis/bigquery/v2.dart';
-import 'package:mockito/annotations.dart';
-import 'package:http/http.dart' as http;
-
-export 'mocks.mocks.dart';
-
-@GenerateMocks(<Type>[
- AccessClientProvider,
- JobsResource,
- ApproverService,
- GitHub,
- PullRequestsService,
- RepositoriesService,
- GitHubComparison,
- http.Response,
-])
-void main() {}
diff --git a/auto_submit/test/utilities/mocks.mocks.dart b/auto_submit/test/utilities/mocks.mocks.dart
deleted file mode 100644
index cfa5a26..0000000
--- a/auto_submit/test/utilities/mocks.mocks.dart
+++ /dev/null
@@ -1,3169 +0,0 @@
-// Mocks generated by Mockito 5.4.2 from annotations
-// in auto_submit/test/utilities/mocks.dart.
-// Do not manually edit this file.
-
-// ignore_for_file: no_leading_underscores_for_library_prefixes
-import 'dart:async' as _i6;
-import 'dart:typed_data' as _i11;
-
-import 'package:_discoveryapis_commons/_discoveryapis_commons.dart' as _i8;
-import 'package:auto_submit/service/access_client_provider.dart' as _i7;
-import 'package:auto_submit/service/approver_service.dart' as _i9;
-import 'package:auto_submit/service/config.dart' as _i4;
-import 'package:github/github.dart' as _i5;
-import 'package:googleapis/bigquery/v2.dart' as _i3;
-import 'package:http/http.dart' as _i2;
-import 'package:mockito/mockito.dart' as _i1;
-import 'package:mockito/src/dummies.dart' as _i10;
-
-// ignore_for_file: type=lint
-// ignore_for_file: avoid_redundant_argument_values
-// ignore_for_file: avoid_setters_without_getters
-// ignore_for_file: comment_references
-// ignore_for_file: implementation_imports
-// ignore_for_file: invalid_use_of_visible_for_testing_member
-// ignore_for_file: prefer_const_constructors
-// ignore_for_file: unnecessary_parenthesis
-// ignore_for_file: camel_case_types
-// ignore_for_file: subtype_of_sealed_class
-
-class _FakeClient_0 extends _i1.SmartFake implements _i2.Client {
- _FakeClient_0(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeJobCancelResponse_1 extends _i1.SmartFake implements _i3.JobCancelResponse {
- _FakeJobCancelResponse_1(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeJob_2 extends _i1.SmartFake implements _i3.Job {
- _FakeJob_2(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeGetQueryResultsResponse_3 extends _i1.SmartFake implements _i3.GetQueryResultsResponse {
- _FakeGetQueryResultsResponse_3(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeJobList_4 extends _i1.SmartFake implements _i3.JobList {
- _FakeJobList_4(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeQueryResponse_5 extends _i1.SmartFake implements _i3.QueryResponse {
- _FakeQueryResponse_5(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeConfig_6 extends _i1.SmartFake implements _i4.Config {
- _FakeConfig_6(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeAuthentication_7 extends _i1.SmartFake implements _i5.Authentication {
- _FakeAuthentication_7(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeActivityService_8 extends _i1.SmartFake implements _i5.ActivityService {
- _FakeActivityService_8(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeAuthorizationsService_9 extends _i1.SmartFake implements _i5.AuthorizationsService {
- _FakeAuthorizationsService_9(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeGistsService_10 extends _i1.SmartFake implements _i5.GistsService {
- _FakeGistsService_10(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeGitService_11 extends _i1.SmartFake implements _i5.GitService {
- _FakeGitService_11(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeIssuesService_12 extends _i1.SmartFake implements _i5.IssuesService {
- _FakeIssuesService_12(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeMiscService_13 extends _i1.SmartFake implements _i5.MiscService {
- _FakeMiscService_13(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeOrganizationsService_14 extends _i1.SmartFake implements _i5.OrganizationsService {
- _FakeOrganizationsService_14(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakePullRequestsService_15 extends _i1.SmartFake implements _i5.PullRequestsService {
- _FakePullRequestsService_15(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeRepositoriesService_16 extends _i1.SmartFake implements _i5.RepositoriesService {
- _FakeRepositoriesService_16(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeSearchService_17 extends _i1.SmartFake implements _i5.SearchService {
- _FakeSearchService_17(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeUrlShortenerService_18 extends _i1.SmartFake implements _i5.UrlShortenerService {
- _FakeUrlShortenerService_18(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeUsersService_19 extends _i1.SmartFake implements _i5.UsersService {
- _FakeUsersService_19(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeChecksService_20 extends _i1.SmartFake implements _i5.ChecksService {
- _FakeChecksService_20(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeFuture_21<T1> extends _i1.SmartFake implements _i6.Future<T1> {
- _FakeFuture_21(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeResponse_22 extends _i1.SmartFake implements _i2.Response {
- _FakeResponse_22(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeGitHub_23 extends _i1.SmartFake implements _i5.GitHub {
- _FakeGitHub_23(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakePullRequest_24 extends _i1.SmartFake implements _i5.PullRequest {
- _FakePullRequest_24(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakePullRequestMerge_25 extends _i1.SmartFake implements _i5.PullRequestMerge {
- _FakePullRequestMerge_25(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakePullRequestComment_26 extends _i1.SmartFake implements _i5.PullRequestComment {
- _FakePullRequestComment_26(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakePullRequestReview_27 extends _i1.SmartFake implements _i5.PullRequestReview {
- _FakePullRequestReview_27(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeRepository_28 extends _i1.SmartFake implements _i5.Repository {
- _FakeRepository_28(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeLicenseDetails_29 extends _i1.SmartFake implements _i5.LicenseDetails {
- _FakeLicenseDetails_29(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeLanguageBreakdown_30 extends _i1.SmartFake implements _i5.LanguageBreakdown {
- _FakeLanguageBreakdown_30(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeBranch_31 extends _i1.SmartFake implements _i5.Branch {
- _FakeBranch_31(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeCommitComment_32 extends _i1.SmartFake implements _i5.CommitComment {
- _FakeCommitComment_32(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeRepositoryCommit_33 extends _i1.SmartFake implements _i5.RepositoryCommit {
- _FakeRepositoryCommit_33(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeGitHubComparison_34 extends _i1.SmartFake implements _i5.GitHubComparison {
- _FakeGitHubComparison_34(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeGitHubFile_35 extends _i1.SmartFake implements _i5.GitHubFile {
- _FakeGitHubFile_35(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeRepositoryContents_36 extends _i1.SmartFake implements _i5.RepositoryContents {
- _FakeRepositoryContents_36(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeContentCreation_37 extends _i1.SmartFake implements _i5.ContentCreation {
- _FakeContentCreation_37(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeHook_38 extends _i1.SmartFake implements _i5.Hook {
- _FakeHook_38(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakePublicKey_39 extends _i1.SmartFake implements _i5.PublicKey {
- _FakePublicKey_39(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeRepositoryPages_40 extends _i1.SmartFake implements _i5.RepositoryPages {
- _FakeRepositoryPages_40(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakePageBuild_41 extends _i1.SmartFake implements _i5.PageBuild {
- _FakePageBuild_41(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeRelease_42 extends _i1.SmartFake implements _i5.Release {
- _FakeRelease_42(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeReleaseAsset_43 extends _i1.SmartFake implements _i5.ReleaseAsset {
- _FakeReleaseAsset_43(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeContributorParticipation_44 extends _i1.SmartFake implements _i5.ContributorParticipation {
- _FakeContributorParticipation_44(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeRepositoryStatus_45 extends _i1.SmartFake implements _i5.RepositoryStatus {
- _FakeRepositoryStatus_45(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeCombinedRepositoryStatus_46 extends _i1.SmartFake implements _i5.CombinedRepositoryStatus {
- _FakeCombinedRepositoryStatus_46(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-class _FakeReleaseNotes_47 extends _i1.SmartFake implements _i5.ReleaseNotes {
- _FakeReleaseNotes_47(
- Object parent,
- Invocation parentInvocation,
- ) : super(
- parent,
- parentInvocation,
- );
-}
-
-/// A class which mocks [AccessClientProvider].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockAccessClientProvider extends _i1.Mock implements _i7.AccessClientProvider {
- MockAccessClientProvider() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i6.Future<_i2.Client> createAccessClient(
- {List<String>? scopes = const [r'https://www.googleapis.com/auth/cloud-platform']}) =>
- (super.noSuchMethod(
- Invocation.method(
- #createAccessClient,
- [],
- {#scopes: scopes},
- ),
- returnValue: _i6.Future<_i2.Client>.value(_FakeClient_0(
- this,
- Invocation.method(
- #createAccessClient,
- [],
- {#scopes: scopes},
- ),
- )),
- ) as _i6.Future<_i2.Client>);
-}
-
-/// A class which mocks [JobsResource].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockJobsResource extends _i1.Mock implements _i3.JobsResource {
- MockJobsResource() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i6.Future<_i3.JobCancelResponse> cancel(
- String? projectId,
- String? jobId, {
- String? location,
- String? $fields,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #cancel,
- [
- projectId,
- jobId,
- ],
- {
- #location: location,
- #$fields: $fields,
- },
- ),
- returnValue: _i6.Future<_i3.JobCancelResponse>.value(_FakeJobCancelResponse_1(
- this,
- Invocation.method(
- #cancel,
- [
- projectId,
- jobId,
- ],
- {
- #location: location,
- #$fields: $fields,
- },
- ),
- )),
- ) as _i6.Future<_i3.JobCancelResponse>);
- @override
- _i6.Future<void> delete(
- String? projectId,
- String? jobId, {
- String? location,
- String? $fields,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #delete,
- [
- projectId,
- jobId,
- ],
- {
- #location: location,
- #$fields: $fields,
- },
- ),
- returnValue: _i6.Future<void>.value(),
- returnValueForMissingStub: _i6.Future<void>.value(),
- ) as _i6.Future<void>);
- @override
- _i6.Future<_i3.Job> get(
- String? projectId,
- String? jobId, {
- String? location,
- String? $fields,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #get,
- [
- projectId,
- jobId,
- ],
- {
- #location: location,
- #$fields: $fields,
- },
- ),
- returnValue: _i6.Future<_i3.Job>.value(_FakeJob_2(
- this,
- Invocation.method(
- #get,
- [
- projectId,
- jobId,
- ],
- {
- #location: location,
- #$fields: $fields,
- },
- ),
- )),
- ) as _i6.Future<_i3.Job>);
- @override
- _i6.Future<_i3.GetQueryResultsResponse> getQueryResults(
- String? projectId,
- String? jobId, {
- String? location,
- int? maxResults,
- String? pageToken,
- String? startIndex,
- int? timeoutMs,
- String? $fields,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #getQueryResults,
- [
- projectId,
- jobId,
- ],
- {
- #location: location,
- #maxResults: maxResults,
- #pageToken: pageToken,
- #startIndex: startIndex,
- #timeoutMs: timeoutMs,
- #$fields: $fields,
- },
- ),
- returnValue: _i6.Future<_i3.GetQueryResultsResponse>.value(_FakeGetQueryResultsResponse_3(
- this,
- Invocation.method(
- #getQueryResults,
- [
- projectId,
- jobId,
- ],
- {
- #location: location,
- #maxResults: maxResults,
- #pageToken: pageToken,
- #startIndex: startIndex,
- #timeoutMs: timeoutMs,
- #$fields: $fields,
- },
- ),
- )),
- ) as _i6.Future<_i3.GetQueryResultsResponse>);
- @override
- _i6.Future<_i3.Job> insert(
- _i3.Job? request,
- String? projectId, {
- String? $fields,
- _i8.UploadOptions? uploadOptions = _i8.UploadOptions.defaultOptions,
- _i8.Media? uploadMedia,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #insert,
- [
- request,
- projectId,
- ],
- {
- #$fields: $fields,
- #uploadOptions: uploadOptions,
- #uploadMedia: uploadMedia,
- },
- ),
- returnValue: _i6.Future<_i3.Job>.value(_FakeJob_2(
- this,
- Invocation.method(
- #insert,
- [
- request,
- projectId,
- ],
- {
- #$fields: $fields,
- #uploadOptions: uploadOptions,
- #uploadMedia: uploadMedia,
- },
- ),
- )),
- ) as _i6.Future<_i3.Job>);
- @override
- _i6.Future<_i3.JobList> list(
- String? projectId, {
- bool? allUsers,
- String? maxCreationTime,
- int? maxResults,
- String? minCreationTime,
- String? pageToken,
- String? parentJobId,
- String? projection,
- List<String>? stateFilter,
- String? $fields,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #list,
- [projectId],
- {
- #allUsers: allUsers,
- #maxCreationTime: maxCreationTime,
- #maxResults: maxResults,
- #minCreationTime: minCreationTime,
- #pageToken: pageToken,
- #parentJobId: parentJobId,
- #projection: projection,
- #stateFilter: stateFilter,
- #$fields: $fields,
- },
- ),
- returnValue: _i6.Future<_i3.JobList>.value(_FakeJobList_4(
- this,
- Invocation.method(
- #list,
- [projectId],
- {
- #allUsers: allUsers,
- #maxCreationTime: maxCreationTime,
- #maxResults: maxResults,
- #minCreationTime: minCreationTime,
- #pageToken: pageToken,
- #parentJobId: parentJobId,
- #projection: projection,
- #stateFilter: stateFilter,
- #$fields: $fields,
- },
- ),
- )),
- ) as _i6.Future<_i3.JobList>);
- @override
- _i6.Future<_i3.QueryResponse> query(
- _i3.QueryRequest? request,
- String? projectId, {
- String? $fields,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #query,
- [
- request,
- projectId,
- ],
- {#$fields: $fields},
- ),
- returnValue: _i6.Future<_i3.QueryResponse>.value(_FakeQueryResponse_5(
- this,
- Invocation.method(
- #query,
- [
- request,
- projectId,
- ],
- {#$fields: $fields},
- ),
- )),
- ) as _i6.Future<_i3.QueryResponse>);
-}
-
-/// A class which mocks [ApproverService].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockApproverService extends _i1.Mock implements _i9.ApproverService {
- MockApproverService() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i4.Config get config => (super.noSuchMethod(
- Invocation.getter(#config),
- returnValue: _FakeConfig_6(
- this,
- Invocation.getter(#config),
- ),
- ) as _i4.Config);
- @override
- _i6.Future<Set<String>> getAutoApprovalAccounts(_i5.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #getAutoApprovalAccounts,
- [slug],
- ),
- returnValue: _i6.Future<Set<String>>.value(<String>{}),
- ) as _i6.Future<Set<String>>);
- @override
- _i6.Future<void> autoApproval(_i5.PullRequest? pullRequest) => (super.noSuchMethod(
- Invocation.method(
- #autoApproval,
- [pullRequest],
- ),
- returnValue: _i6.Future<void>.value(),
- returnValueForMissingStub: _i6.Future<void>.value(),
- ) as _i6.Future<void>);
-}
-
-/// A class which mocks [GitHub].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockGitHub extends _i1.Mock implements _i5.GitHub {
- MockGitHub() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i5.Authentication get auth => (super.noSuchMethod(
- Invocation.getter(#auth),
- returnValue: _FakeAuthentication_7(
- this,
- Invocation.getter(#auth),
- ),
- ) as _i5.Authentication);
- @override
- set auth(_i5.Authentication? _auth) => super.noSuchMethod(
- Invocation.setter(
- #auth,
- _auth,
- ),
- returnValueForMissingStub: null,
- );
- @override
- String get endpoint => (super.noSuchMethod(
- Invocation.getter(#endpoint),
- returnValue: '',
- ) as String);
- @override
- String get version => (super.noSuchMethod(
- Invocation.getter(#version),
- returnValue: '',
- ) as String);
- @override
- _i2.Client get client => (super.noSuchMethod(
- Invocation.getter(#client),
- returnValue: _FakeClient_0(
- this,
- Invocation.getter(#client),
- ),
- ) as _i2.Client);
- @override
- _i5.ActivityService get activity => (super.noSuchMethod(
- Invocation.getter(#activity),
- returnValue: _FakeActivityService_8(
- this,
- Invocation.getter(#activity),
- ),
- ) as _i5.ActivityService);
- @override
- _i5.AuthorizationsService get authorizations => (super.noSuchMethod(
- Invocation.getter(#authorizations),
- returnValue: _FakeAuthorizationsService_9(
- this,
- Invocation.getter(#authorizations),
- ),
- ) as _i5.AuthorizationsService);
- @override
- _i5.GistsService get gists => (super.noSuchMethod(
- Invocation.getter(#gists),
- returnValue: _FakeGistsService_10(
- this,
- Invocation.getter(#gists),
- ),
- ) as _i5.GistsService);
- @override
- _i5.GitService get git => (super.noSuchMethod(
- Invocation.getter(#git),
- returnValue: _FakeGitService_11(
- this,
- Invocation.getter(#git),
- ),
- ) as _i5.GitService);
- @override
- _i5.IssuesService get issues => (super.noSuchMethod(
- Invocation.getter(#issues),
- returnValue: _FakeIssuesService_12(
- this,
- Invocation.getter(#issues),
- ),
- ) as _i5.IssuesService);
- @override
- _i5.MiscService get misc => (super.noSuchMethod(
- Invocation.getter(#misc),
- returnValue: _FakeMiscService_13(
- this,
- Invocation.getter(#misc),
- ),
- ) as _i5.MiscService);
- @override
- _i5.OrganizationsService get organizations => (super.noSuchMethod(
- Invocation.getter(#organizations),
- returnValue: _FakeOrganizationsService_14(
- this,
- Invocation.getter(#organizations),
- ),
- ) as _i5.OrganizationsService);
- @override
- _i5.PullRequestsService get pullRequests => (super.noSuchMethod(
- Invocation.getter(#pullRequests),
- returnValue: _FakePullRequestsService_15(
- this,
- Invocation.getter(#pullRequests),
- ),
- ) as _i5.PullRequestsService);
- @override
- _i5.RepositoriesService get repositories => (super.noSuchMethod(
- Invocation.getter(#repositories),
- returnValue: _FakeRepositoriesService_16(
- this,
- Invocation.getter(#repositories),
- ),
- ) as _i5.RepositoriesService);
- @override
- _i5.SearchService get search => (super.noSuchMethod(
- Invocation.getter(#search),
- returnValue: _FakeSearchService_17(
- this,
- Invocation.getter(#search),
- ),
- ) as _i5.SearchService);
- @override
- _i5.UrlShortenerService get urlShortener => (super.noSuchMethod(
- Invocation.getter(#urlShortener),
- returnValue: _FakeUrlShortenerService_18(
- this,
- Invocation.getter(#urlShortener),
- ),
- ) as _i5.UrlShortenerService);
- @override
- _i5.UsersService get users => (super.noSuchMethod(
- Invocation.getter(#users),
- returnValue: _FakeUsersService_19(
- this,
- Invocation.getter(#users),
- ),
- ) as _i5.UsersService);
- @override
- _i5.ChecksService get checks => (super.noSuchMethod(
- Invocation.getter(#checks),
- returnValue: _FakeChecksService_20(
- this,
- Invocation.getter(#checks),
- ),
- ) as _i5.ChecksService);
- @override
- _i6.Future<T> getJSON<S, T>(
- String? path, {
- int? statusCode,
- void Function(_i2.Response)? fail,
- Map<String, String>? headers,
- Map<String, String>? params,
- _i5.JSONConverter<S, T>? convert,
- String? preview,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #getJSON,
- [path],
- {
- #statusCode: statusCode,
- #fail: fail,
- #headers: headers,
- #params: params,
- #convert: convert,
- #preview: preview,
- },
- ),
- returnValue: _i10.ifNotNull(
- _i10.dummyValueOrNull<T>(
- this,
- Invocation.method(
- #getJSON,
- [path],
- {
- #statusCode: statusCode,
- #fail: fail,
- #headers: headers,
- #params: params,
- #convert: convert,
- #preview: preview,
- },
- ),
- ),
- (T v) => _i6.Future<T>.value(v),
- ) ??
- _FakeFuture_21<T>(
- this,
- Invocation.method(
- #getJSON,
- [path],
- {
- #statusCode: statusCode,
- #fail: fail,
- #headers: headers,
- #params: params,
- #convert: convert,
- #preview: preview,
- },
- ),
- ),
- ) as _i6.Future<T>);
- @override
- _i6.Future<T> postJSON<S, T>(
- String? path, {
- int? statusCode,
- void Function(_i2.Response)? fail,
- Map<String, String>? headers,
- Map<String, dynamic>? params,
- _i5.JSONConverter<S, T>? convert,
- dynamic body,
- String? preview,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #postJSON,
- [path],
- {
- #statusCode: statusCode,
- #fail: fail,
- #headers: headers,
- #params: params,
- #convert: convert,
- #body: body,
- #preview: preview,
- },
- ),
- returnValue: _i10.ifNotNull(
- _i10.dummyValueOrNull<T>(
- this,
- Invocation.method(
- #postJSON,
- [path],
- {
- #statusCode: statusCode,
- #fail: fail,
- #headers: headers,
- #params: params,
- #convert: convert,
- #body: body,
- #preview: preview,
- },
- ),
- ),
- (T v) => _i6.Future<T>.value(v),
- ) ??
- _FakeFuture_21<T>(
- this,
- Invocation.method(
- #postJSON,
- [path],
- {
- #statusCode: statusCode,
- #fail: fail,
- #headers: headers,
- #params: params,
- #convert: convert,
- #body: body,
- #preview: preview,
- },
- ),
- ),
- ) as _i6.Future<T>);
- @override
- _i6.Future<T> putJSON<S, T>(
- String? path, {
- int? statusCode,
- void Function(_i2.Response)? fail,
- Map<String, String>? headers,
- Map<String, dynamic>? params,
- _i5.JSONConverter<S, T>? convert,
- dynamic body,
- String? preview,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #putJSON,
- [path],
- {
- #statusCode: statusCode,
- #fail: fail,
- #headers: headers,
- #params: params,
- #convert: convert,
- #body: body,
- #preview: preview,
- },
- ),
- returnValue: _i10.ifNotNull(
- _i10.dummyValueOrNull<T>(
- this,
- Invocation.method(
- #putJSON,
- [path],
- {
- #statusCode: statusCode,
- #fail: fail,
- #headers: headers,
- #params: params,
- #convert: convert,
- #body: body,
- #preview: preview,
- },
- ),
- ),
- (T v) => _i6.Future<T>.value(v),
- ) ??
- _FakeFuture_21<T>(
- this,
- Invocation.method(
- #putJSON,
- [path],
- {
- #statusCode: statusCode,
- #fail: fail,
- #headers: headers,
- #params: params,
- #convert: convert,
- #body: body,
- #preview: preview,
- },
- ),
- ),
- ) as _i6.Future<T>);
- @override
- _i6.Future<T> patchJSON<S, T>(
- String? path, {
- int? statusCode,
- void Function(_i2.Response)? fail,
- Map<String, String>? headers,
- Map<String, dynamic>? params,
- _i5.JSONConverter<S, T>? convert,
- dynamic body,
- String? preview,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #patchJSON,
- [path],
- {
- #statusCode: statusCode,
- #fail: fail,
- #headers: headers,
- #params: params,
- #convert: convert,
- #body: body,
- #preview: preview,
- },
- ),
- returnValue: _i10.ifNotNull(
- _i10.dummyValueOrNull<T>(
- this,
- Invocation.method(
- #patchJSON,
- [path],
- {
- #statusCode: statusCode,
- #fail: fail,
- #headers: headers,
- #params: params,
- #convert: convert,
- #body: body,
- #preview: preview,
- },
- ),
- ),
- (T v) => _i6.Future<T>.value(v),
- ) ??
- _FakeFuture_21<T>(
- this,
- Invocation.method(
- #patchJSON,
- [path],
- {
- #statusCode: statusCode,
- #fail: fail,
- #headers: headers,
- #params: params,
- #convert: convert,
- #body: body,
- #preview: preview,
- },
- ),
- ),
- ) as _i6.Future<T>);
- @override
- _i6.Future<T> requestJson<S, T>(
- String? method,
- String? path, {
- int? statusCode,
- void Function(_i2.Response)? fail,
- Map<String, String>? headers,
- Map<String, dynamic>? params,
- _i5.JSONConverter<S, T?>? convert,
- dynamic body,
- String? preview,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #requestJson,
- [
- method,
- path,
- ],
- {
- #statusCode: statusCode,
- #fail: fail,
- #headers: headers,
- #params: params,
- #convert: convert,
- #body: body,
- #preview: preview,
- },
- ),
- returnValue: _i10.ifNotNull(
- _i10.dummyValueOrNull<T>(
- this,
- Invocation.method(
- #requestJson,
- [
- method,
- path,
- ],
- {
- #statusCode: statusCode,
- #fail: fail,
- #headers: headers,
- #params: params,
- #convert: convert,
- #body: body,
- #preview: preview,
- },
- ),
- ),
- (T v) => _i6.Future<T>.value(v),
- ) ??
- _FakeFuture_21<T>(
- this,
- Invocation.method(
- #requestJson,
- [
- method,
- path,
- ],
- {
- #statusCode: statusCode,
- #fail: fail,
- #headers: headers,
- #params: params,
- #convert: convert,
- #body: body,
- #preview: preview,
- },
- ),
- ),
- ) as _i6.Future<T>);
- @override
- _i6.Future<_i2.Response> request(
- String? method,
- String? path, {
- Map<String, String>? headers,
- Map<String, dynamic>? params,
- dynamic body,
- int? statusCode,
- void Function(_i2.Response)? fail,
- String? preview,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #request,
- [
- method,
- path,
- ],
- {
- #headers: headers,
- #params: params,
- #body: body,
- #statusCode: statusCode,
- #fail: fail,
- #preview: preview,
- },
- ),
- returnValue: _i6.Future<_i2.Response>.value(_FakeResponse_22(
- this,
- Invocation.method(
- #request,
- [
- method,
- path,
- ],
- {
- #headers: headers,
- #params: params,
- #body: body,
- #statusCode: statusCode,
- #fail: fail,
- #preview: preview,
- },
- ),
- )),
- ) as _i6.Future<_i2.Response>);
- @override
- Never handleStatusCode(_i2.Response? response) => (super.noSuchMethod(
- Invocation.method(
- #handleStatusCode,
- [response],
- ),
- returnValue: null,
- ) as Never);
- @override
- void dispose() => super.noSuchMethod(
- Invocation.method(
- #dispose,
- [],
- ),
- returnValueForMissingStub: null,
- );
-}
-
-/// A class which mocks [PullRequestsService].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockPullRequestsService extends _i1.Mock implements _i5.PullRequestsService {
- MockPullRequestsService() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i5.GitHub get github => (super.noSuchMethod(
- Invocation.getter(#github),
- returnValue: _FakeGitHub_23(
- this,
- Invocation.getter(#github),
- ),
- ) as _i5.GitHub);
- @override
- _i6.Stream<_i5.PullRequest> list(
- _i5.RepositorySlug? slug, {
- int? pages,
- String? base,
- String? direction = r'desc',
- String? head,
- String? sort = r'created',
- String? state = r'open',
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #list,
- [slug],
- {
- #pages: pages,
- #base: base,
- #direction: direction,
- #head: head,
- #sort: sort,
- #state: state,
- },
- ),
- returnValue: _i6.Stream<_i5.PullRequest>.empty(),
- ) as _i6.Stream<_i5.PullRequest>);
- @override
- _i6.Future<_i5.PullRequest> get(
- _i5.RepositorySlug? slug,
- int? number,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #get,
- [
- slug,
- number,
- ],
- ),
- returnValue: _i6.Future<_i5.PullRequest>.value(_FakePullRequest_24(
- this,
- Invocation.method(
- #get,
- [
- slug,
- number,
- ],
- ),
- )),
- ) as _i6.Future<_i5.PullRequest>);
- @override
- _i6.Future<_i5.PullRequest> create(
- _i5.RepositorySlug? slug,
- _i5.CreatePullRequest? request,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #create,
- [
- slug,
- request,
- ],
- ),
- returnValue: _i6.Future<_i5.PullRequest>.value(_FakePullRequest_24(
- this,
- Invocation.method(
- #create,
- [
- slug,
- request,
- ],
- ),
- )),
- ) as _i6.Future<_i5.PullRequest>);
- @override
- _i6.Future<_i5.PullRequest> edit(
- _i5.RepositorySlug? slug,
- int? number, {
- String? title,
- String? body,
- String? state,
- String? base,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #edit,
- [
- slug,
- number,
- ],
- {
- #title: title,
- #body: body,
- #state: state,
- #base: base,
- },
- ),
- returnValue: _i6.Future<_i5.PullRequest>.value(_FakePullRequest_24(
- this,
- Invocation.method(
- #edit,
- [
- slug,
- number,
- ],
- {
- #title: title,
- #body: body,
- #state: state,
- #base: base,
- },
- ),
- )),
- ) as _i6.Future<_i5.PullRequest>);
- @override
- _i6.Stream<_i5.RepositoryCommit> listCommits(
- _i5.RepositorySlug? slug,
- int? number,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #listCommits,
- [
- slug,
- number,
- ],
- ),
- returnValue: _i6.Stream<_i5.RepositoryCommit>.empty(),
- ) as _i6.Stream<_i5.RepositoryCommit>);
- @override
- _i6.Stream<_i5.PullRequestFile> listFiles(
- _i5.RepositorySlug? slug,
- int? number,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #listFiles,
- [
- slug,
- number,
- ],
- ),
- returnValue: _i6.Stream<_i5.PullRequestFile>.empty(),
- ) as _i6.Stream<_i5.PullRequestFile>);
- @override
- _i6.Stream<_i5.PullRequestReview> listReviews(
- _i5.RepositorySlug? slug,
- int? number,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #listReviews,
- [
- slug,
- number,
- ],
- ),
- returnValue: _i6.Stream<_i5.PullRequestReview>.empty(),
- ) as _i6.Stream<_i5.PullRequestReview>);
- @override
- _i6.Future<bool> isMerged(
- _i5.RepositorySlug? slug,
- int? number,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #isMerged,
- [
- slug,
- number,
- ],
- ),
- returnValue: _i6.Future<bool>.value(false),
- ) as _i6.Future<bool>);
- @override
- _i6.Future<_i5.PullRequestMerge> merge(
- _i5.RepositorySlug? slug,
- int? number, {
- String? message,
- _i5.MergeMethod? mergeMethod = _i5.MergeMethod.merge,
- String? requestSha,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #merge,
- [
- slug,
- number,
- ],
- {
- #message: message,
- #mergeMethod: mergeMethod,
- #requestSha: requestSha,
- },
- ),
- returnValue: _i6.Future<_i5.PullRequestMerge>.value(_FakePullRequestMerge_25(
- this,
- Invocation.method(
- #merge,
- [
- slug,
- number,
- ],
- {
- #message: message,
- #mergeMethod: mergeMethod,
- #requestSha: requestSha,
- },
- ),
- )),
- ) as _i6.Future<_i5.PullRequestMerge>);
- @override
- _i6.Stream<_i5.PullRequestComment> listCommentsByPullRequest(
- _i5.RepositorySlug? slug,
- int? number,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #listCommentsByPullRequest,
- [
- slug,
- number,
- ],
- ),
- returnValue: _i6.Stream<_i5.PullRequestComment>.empty(),
- ) as _i6.Stream<_i5.PullRequestComment>);
- @override
- _i6.Stream<_i5.PullRequestComment> listComments(_i5.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #listComments,
- [slug],
- ),
- returnValue: _i6.Stream<_i5.PullRequestComment>.empty(),
- ) as _i6.Stream<_i5.PullRequestComment>);
- @override
- _i6.Future<_i5.PullRequestComment> createComment(
- _i5.RepositorySlug? slug,
- int? number,
- _i5.CreatePullRequestComment? comment,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #createComment,
- [
- slug,
- number,
- comment,
- ],
- ),
- returnValue: _i6.Future<_i5.PullRequestComment>.value(_FakePullRequestComment_26(
- this,
- Invocation.method(
- #createComment,
- [
- slug,
- number,
- comment,
- ],
- ),
- )),
- ) as _i6.Future<_i5.PullRequestComment>);
- @override
- _i6.Future<_i5.PullRequestReview> createReview(
- _i5.RepositorySlug? slug,
- _i5.CreatePullRequestReview? review,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #createReview,
- [
- slug,
- review,
- ],
- ),
- returnValue: _i6.Future<_i5.PullRequestReview>.value(_FakePullRequestReview_27(
- this,
- Invocation.method(
- #createReview,
- [
- slug,
- review,
- ],
- ),
- )),
- ) as _i6.Future<_i5.PullRequestReview>);
-}
-
-/// A class which mocks [RepositoriesService].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockRepositoriesService extends _i1.Mock implements _i5.RepositoriesService {
- MockRepositoriesService() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i5.GitHub get github => (super.noSuchMethod(
- Invocation.getter(#github),
- returnValue: _FakeGitHub_23(
- this,
- Invocation.getter(#github),
- ),
- ) as _i5.GitHub);
- @override
- _i6.Stream<_i5.Repository> listRepositories({
- String? type = r'owner',
- String? sort = r'full_name',
- String? direction = r'asc',
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #listRepositories,
- [],
- {
- #type: type,
- #sort: sort,
- #direction: direction,
- },
- ),
- returnValue: _i6.Stream<_i5.Repository>.empty(),
- ) as _i6.Stream<_i5.Repository>);
- @override
- _i6.Stream<_i5.Repository> listUserRepositories(
- String? user, {
- String? type = r'owner',
- String? sort = r'full_name',
- String? direction = r'asc',
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #listUserRepositories,
- [user],
- {
- #type: type,
- #sort: sort,
- #direction: direction,
- },
- ),
- returnValue: _i6.Stream<_i5.Repository>.empty(),
- ) as _i6.Stream<_i5.Repository>);
- @override
- _i6.Stream<_i5.Repository> listOrganizationRepositories(
- String? org, {
- String? type = r'all',
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #listOrganizationRepositories,
- [org],
- {#type: type},
- ),
- returnValue: _i6.Stream<_i5.Repository>.empty(),
- ) as _i6.Stream<_i5.Repository>);
- @override
- _i6.Stream<_i5.Repository> listPublicRepositories({
- int? limit = 50,
- DateTime? since,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #listPublicRepositories,
- [],
- {
- #limit: limit,
- #since: since,
- },
- ),
- returnValue: _i6.Stream<_i5.Repository>.empty(),
- ) as _i6.Stream<_i5.Repository>);
- @override
- _i6.Future<_i5.Repository> createRepository(
- _i5.CreateRepository? repository, {
- String? org,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #createRepository,
- [repository],
- {#org: org},
- ),
- returnValue: _i6.Future<_i5.Repository>.value(_FakeRepository_28(
- this,
- Invocation.method(
- #createRepository,
- [repository],
- {#org: org},
- ),
- )),
- ) as _i6.Future<_i5.Repository>);
- @override
- _i6.Future<_i5.LicenseDetails> getLicense(_i5.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #getLicense,
- [slug],
- ),
- returnValue: _i6.Future<_i5.LicenseDetails>.value(_FakeLicenseDetails_29(
- this,
- Invocation.method(
- #getLicense,
- [slug],
- ),
- )),
- ) as _i6.Future<_i5.LicenseDetails>);
- @override
- _i6.Future<_i5.Repository> getRepository(_i5.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #getRepository,
- [slug],
- ),
- returnValue: _i6.Future<_i5.Repository>.value(_FakeRepository_28(
- this,
- Invocation.method(
- #getRepository,
- [slug],
- ),
- )),
- ) as _i6.Future<_i5.Repository>);
- @override
- _i6.Stream<_i5.Repository> getRepositories(List<_i5.RepositorySlug>? slugs) => (super.noSuchMethod(
- Invocation.method(
- #getRepositories,
- [slugs],
- ),
- returnValue: _i6.Stream<_i5.Repository>.empty(),
- ) as _i6.Stream<_i5.Repository>);
- @override
- _i6.Future<_i5.Repository> editRepository(
- _i5.RepositorySlug? slug, {
- String? name,
- String? description,
- String? homepage,
- bool? private,
- bool? hasIssues,
- bool? hasWiki,
- bool? hasDownloads,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #editRepository,
- [slug],
- {
- #name: name,
- #description: description,
- #homepage: homepage,
- #private: private,
- #hasIssues: hasIssues,
- #hasWiki: hasWiki,
- #hasDownloads: hasDownloads,
- },
- ),
- returnValue: _i6.Future<_i5.Repository>.value(_FakeRepository_28(
- this,
- Invocation.method(
- #editRepository,
- [slug],
- {
- #name: name,
- #description: description,
- #homepage: homepage,
- #private: private,
- #hasIssues: hasIssues,
- #hasWiki: hasWiki,
- #hasDownloads: hasDownloads,
- },
- ),
- )),
- ) as _i6.Future<_i5.Repository>);
- @override
- _i6.Future<bool> deleteRepository(_i5.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #deleteRepository,
- [slug],
- ),
- returnValue: _i6.Future<bool>.value(false),
- ) as _i6.Future<bool>);
- @override
- _i6.Stream<_i5.Contributor> listContributors(
- _i5.RepositorySlug? slug, {
- bool? anon = false,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #listContributors,
- [slug],
- {#anon: anon},
- ),
- returnValue: _i6.Stream<_i5.Contributor>.empty(),
- ) as _i6.Stream<_i5.Contributor>);
- @override
- _i6.Stream<_i5.Team> listTeams(_i5.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #listTeams,
- [slug],
- ),
- returnValue: _i6.Stream<_i5.Team>.empty(),
- ) as _i6.Stream<_i5.Team>);
- @override
- _i6.Future<_i5.LanguageBreakdown> listLanguages(_i5.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #listLanguages,
- [slug],
- ),
- returnValue: _i6.Future<_i5.LanguageBreakdown>.value(_FakeLanguageBreakdown_30(
- this,
- Invocation.method(
- #listLanguages,
- [slug],
- ),
- )),
- ) as _i6.Future<_i5.LanguageBreakdown>);
- @override
- _i6.Stream<_i5.Tag> listTags(
- _i5.RepositorySlug? slug, {
- int? page = 1,
- int? pages,
- int? perPage = 30,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #listTags,
- [slug],
- {
- #page: page,
- #pages: pages,
- #perPage: perPage,
- },
- ),
- returnValue: _i6.Stream<_i5.Tag>.empty(),
- ) as _i6.Stream<_i5.Tag>);
- @override
- _i6.Stream<_i5.Branch> listBranches(_i5.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #listBranches,
- [slug],
- ),
- returnValue: _i6.Stream<_i5.Branch>.empty(),
- ) as _i6.Stream<_i5.Branch>);
- @override
- _i6.Future<_i5.Branch> getBranch(
- _i5.RepositorySlug? slug,
- String? branch,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #getBranch,
- [
- slug,
- branch,
- ],
- ),
- returnValue: _i6.Future<_i5.Branch>.value(_FakeBranch_31(
- this,
- Invocation.method(
- #getBranch,
- [
- slug,
- branch,
- ],
- ),
- )),
- ) as _i6.Future<_i5.Branch>);
- @override
- _i6.Stream<_i5.Collaborator> listCollaborators(_i5.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #listCollaborators,
- [slug],
- ),
- returnValue: _i6.Stream<_i5.Collaborator>.empty(),
- ) as _i6.Stream<_i5.Collaborator>);
- @override
- _i6.Future<bool> isCollaborator(
- _i5.RepositorySlug? slug,
- String? user,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #isCollaborator,
- [
- slug,
- user,
- ],
- ),
- returnValue: _i6.Future<bool>.value(false),
- ) as _i6.Future<bool>);
- @override
- _i6.Future<bool> addCollaborator(
- _i5.RepositorySlug? slug,
- String? user,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #addCollaborator,
- [
- slug,
- user,
- ],
- ),
- returnValue: _i6.Future<bool>.value(false),
- ) as _i6.Future<bool>);
- @override
- _i6.Future<bool> removeCollaborator(
- _i5.RepositorySlug? slug,
- String? user,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #removeCollaborator,
- [
- slug,
- user,
- ],
- ),
- returnValue: _i6.Future<bool>.value(false),
- ) as _i6.Future<bool>);
- @override
- _i6.Stream<_i5.CommitComment> listSingleCommitComments(
- _i5.RepositorySlug? slug,
- _i5.RepositoryCommit? commit,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #listSingleCommitComments,
- [
- slug,
- commit,
- ],
- ),
- returnValue: _i6.Stream<_i5.CommitComment>.empty(),
- ) as _i6.Stream<_i5.CommitComment>);
- @override
- _i6.Stream<_i5.CommitComment> listCommitComments(_i5.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #listCommitComments,
- [slug],
- ),
- returnValue: _i6.Stream<_i5.CommitComment>.empty(),
- ) as _i6.Stream<_i5.CommitComment>);
- @override
- _i6.Future<_i5.CommitComment> createCommitComment(
- _i5.RepositorySlug? slug,
- _i5.RepositoryCommit? commit, {
- required String? body,
- String? path,
- int? position,
- int? line,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #createCommitComment,
- [
- slug,
- commit,
- ],
- {
- #body: body,
- #path: path,
- #position: position,
- #line: line,
- },
- ),
- returnValue: _i6.Future<_i5.CommitComment>.value(_FakeCommitComment_32(
- this,
- Invocation.method(
- #createCommitComment,
- [
- slug,
- commit,
- ],
- {
- #body: body,
- #path: path,
- #position: position,
- #line: line,
- },
- ),
- )),
- ) as _i6.Future<_i5.CommitComment>);
- @override
- _i6.Future<_i5.CommitComment> getCommitComment(
- _i5.RepositorySlug? slug, {
- required int? id,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #getCommitComment,
- [slug],
- {#id: id},
- ),
- returnValue: _i6.Future<_i5.CommitComment>.value(_FakeCommitComment_32(
- this,
- Invocation.method(
- #getCommitComment,
- [slug],
- {#id: id},
- ),
- )),
- ) as _i6.Future<_i5.CommitComment>);
- @override
- _i6.Future<_i5.CommitComment> updateCommitComment(
- _i5.RepositorySlug? slug, {
- required int? id,
- required String? body,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #updateCommitComment,
- [slug],
- {
- #id: id,
- #body: body,
- },
- ),
- returnValue: _i6.Future<_i5.CommitComment>.value(_FakeCommitComment_32(
- this,
- Invocation.method(
- #updateCommitComment,
- [slug],
- {
- #id: id,
- #body: body,
- },
- ),
- )),
- ) as _i6.Future<_i5.CommitComment>);
- @override
- _i6.Future<bool> deleteCommitComment(
- _i5.RepositorySlug? slug, {
- required int? id,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #deleteCommitComment,
- [slug],
- {#id: id},
- ),
- returnValue: _i6.Future<bool>.value(false),
- ) as _i6.Future<bool>);
- @override
- _i6.Stream<_i5.RepositoryCommit> listCommits(
- _i5.RepositorySlug? slug, {
- String? sha,
- String? path,
- String? author,
- String? committer,
- DateTime? since,
- DateTime? until,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #listCommits,
- [slug],
- {
- #sha: sha,
- #path: path,
- #author: author,
- #committer: committer,
- #since: since,
- #until: until,
- },
- ),
- returnValue: _i6.Stream<_i5.RepositoryCommit>.empty(),
- ) as _i6.Stream<_i5.RepositoryCommit>);
- @override
- _i6.Future<_i5.RepositoryCommit> getCommit(
- _i5.RepositorySlug? slug,
- String? sha,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #getCommit,
- [
- slug,
- sha,
- ],
- ),
- returnValue: _i6.Future<_i5.RepositoryCommit>.value(_FakeRepositoryCommit_33(
- this,
- Invocation.method(
- #getCommit,
- [
- slug,
- sha,
- ],
- ),
- )),
- ) as _i6.Future<_i5.RepositoryCommit>);
- @override
- _i6.Future<String> getCommitDiff(
- _i5.RepositorySlug? slug,
- String? sha,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #getCommitDiff,
- [
- slug,
- sha,
- ],
- ),
- returnValue: _i6.Future<String>.value(''),
- ) as _i6.Future<String>);
- @override
- _i6.Future<_i5.GitHubComparison> compareCommits(
- _i5.RepositorySlug? slug,
- String? refBase,
- String? refHead,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #compareCommits,
- [
- slug,
- refBase,
- refHead,
- ],
- ),
- returnValue: _i6.Future<_i5.GitHubComparison>.value(_FakeGitHubComparison_34(
- this,
- Invocation.method(
- #compareCommits,
- [
- slug,
- refBase,
- refHead,
- ],
- ),
- )),
- ) as _i6.Future<_i5.GitHubComparison>);
- @override
- _i6.Future<_i5.GitHubFile> getReadme(
- _i5.RepositorySlug? slug, {
- String? ref,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #getReadme,
- [slug],
- {#ref: ref},
- ),
- returnValue: _i6.Future<_i5.GitHubFile>.value(_FakeGitHubFile_35(
- this,
- Invocation.method(
- #getReadme,
- [slug],
- {#ref: ref},
- ),
- )),
- ) as _i6.Future<_i5.GitHubFile>);
- @override
- _i6.Future<_i5.RepositoryContents> getContents(
- _i5.RepositorySlug? slug,
- String? path, {
- String? ref,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #getContents,
- [
- slug,
- path,
- ],
- {#ref: ref},
- ),
- returnValue: _i6.Future<_i5.RepositoryContents>.value(_FakeRepositoryContents_36(
- this,
- Invocation.method(
- #getContents,
- [
- slug,
- path,
- ],
- {#ref: ref},
- ),
- )),
- ) as _i6.Future<_i5.RepositoryContents>);
- @override
- _i6.Future<_i5.ContentCreation> createFile(
- _i5.RepositorySlug? slug,
- _i5.CreateFile? file,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #createFile,
- [
- slug,
- file,
- ],
- ),
- returnValue: _i6.Future<_i5.ContentCreation>.value(_FakeContentCreation_37(
- this,
- Invocation.method(
- #createFile,
- [
- slug,
- file,
- ],
- ),
- )),
- ) as _i6.Future<_i5.ContentCreation>);
- @override
- _i6.Future<_i5.ContentCreation> updateFile(
- _i5.RepositorySlug? slug,
- String? path,
- String? message,
- String? content,
- String? sha, {
- String? branch,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #updateFile,
- [
- slug,
- path,
- message,
- content,
- sha,
- ],
- {#branch: branch},
- ),
- returnValue: _i6.Future<_i5.ContentCreation>.value(_FakeContentCreation_37(
- this,
- Invocation.method(
- #updateFile,
- [
- slug,
- path,
- message,
- content,
- sha,
- ],
- {#branch: branch},
- ),
- )),
- ) as _i6.Future<_i5.ContentCreation>);
- @override
- _i6.Future<_i5.ContentCreation> deleteFile(
- _i5.RepositorySlug? slug,
- String? path,
- String? message,
- String? sha,
- String? branch,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #deleteFile,
- [
- slug,
- path,
- message,
- sha,
- branch,
- ],
- ),
- returnValue: _i6.Future<_i5.ContentCreation>.value(_FakeContentCreation_37(
- this,
- Invocation.method(
- #deleteFile,
- [
- slug,
- path,
- message,
- sha,
- branch,
- ],
- ),
- )),
- ) as _i6.Future<_i5.ContentCreation>);
- @override
- _i6.Future<String?> getArchiveLink(
- _i5.RepositorySlug? slug,
- String? ref, {
- String? format = r'tarball',
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #getArchiveLink,
- [
- slug,
- ref,
- ],
- {#format: format},
- ),
- returnValue: _i6.Future<String?>.value(),
- ) as _i6.Future<String?>);
- @override
- _i6.Stream<_i5.Repository> listForks(_i5.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #listForks,
- [slug],
- ),
- returnValue: _i6.Stream<_i5.Repository>.empty(),
- ) as _i6.Stream<_i5.Repository>);
- @override
- _i6.Future<_i5.Repository> createFork(
- _i5.RepositorySlug? slug, [
- _i5.CreateFork? fork,
- ]) =>
- (super.noSuchMethod(
- Invocation.method(
- #createFork,
- [
- slug,
- fork,
- ],
- ),
- returnValue: _i6.Future<_i5.Repository>.value(_FakeRepository_28(
- this,
- Invocation.method(
- #createFork,
- [
- slug,
- fork,
- ],
- ),
- )),
- ) as _i6.Future<_i5.Repository>);
- @override
- _i6.Stream<_i5.Hook> listHooks(_i5.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #listHooks,
- [slug],
- ),
- returnValue: _i6.Stream<_i5.Hook>.empty(),
- ) as _i6.Stream<_i5.Hook>);
- @override
- _i6.Future<_i5.Hook> getHook(
- _i5.RepositorySlug? slug,
- int? id,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #getHook,
- [
- slug,
- id,
- ],
- ),
- returnValue: _i6.Future<_i5.Hook>.value(_FakeHook_38(
- this,
- Invocation.method(
- #getHook,
- [
- slug,
- id,
- ],
- ),
- )),
- ) as _i6.Future<_i5.Hook>);
- @override
- _i6.Future<_i5.Hook> createHook(
- _i5.RepositorySlug? slug,
- _i5.CreateHook? hook,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #createHook,
- [
- slug,
- hook,
- ],
- ),
- returnValue: _i6.Future<_i5.Hook>.value(_FakeHook_38(
- this,
- Invocation.method(
- #createHook,
- [
- slug,
- hook,
- ],
- ),
- )),
- ) as _i6.Future<_i5.Hook>);
- @override
- _i6.Future<_i5.Hook> editHook(
- _i5.RepositorySlug? slug,
- _i5.Hook? hookToEdit, {
- String? configUrl,
- String? configContentType,
- String? configSecret,
- bool? configInsecureSsl,
- List<String>? events,
- List<String>? addEvents,
- List<String>? removeEvents,
- bool? active,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #editHook,
- [
- slug,
- hookToEdit,
- ],
- {
- #configUrl: configUrl,
- #configContentType: configContentType,
- #configSecret: configSecret,
- #configInsecureSsl: configInsecureSsl,
- #events: events,
- #addEvents: addEvents,
- #removeEvents: removeEvents,
- #active: active,
- },
- ),
- returnValue: _i6.Future<_i5.Hook>.value(_FakeHook_38(
- this,
- Invocation.method(
- #editHook,
- [
- slug,
- hookToEdit,
- ],
- {
- #configUrl: configUrl,
- #configContentType: configContentType,
- #configSecret: configSecret,
- #configInsecureSsl: configInsecureSsl,
- #events: events,
- #addEvents: addEvents,
- #removeEvents: removeEvents,
- #active: active,
- },
- ),
- )),
- ) as _i6.Future<_i5.Hook>);
- @override
- _i6.Future<bool> testPushHook(
- _i5.RepositorySlug? slug,
- int? id,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #testPushHook,
- [
- slug,
- id,
- ],
- ),
- returnValue: _i6.Future<bool>.value(false),
- ) as _i6.Future<bool>);
- @override
- _i6.Future<bool> pingHook(
- _i5.RepositorySlug? slug,
- int? id,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #pingHook,
- [
- slug,
- id,
- ],
- ),
- returnValue: _i6.Future<bool>.value(false),
- ) as _i6.Future<bool>);
- @override
- _i6.Future<bool> deleteHook(
- _i5.RepositorySlug? slug,
- int? id,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #deleteHook,
- [
- slug,
- id,
- ],
- ),
- returnValue: _i6.Future<bool>.value(false),
- ) as _i6.Future<bool>);
- @override
- _i6.Stream<_i5.PublicKey> listDeployKeys(_i5.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #listDeployKeys,
- [slug],
- ),
- returnValue: _i6.Stream<_i5.PublicKey>.empty(),
- ) as _i6.Stream<_i5.PublicKey>);
- @override
- _i6.Future<_i5.PublicKey> getDeployKey(
- _i5.RepositorySlug? slug, {
- required int? id,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #getDeployKey,
- [slug],
- {#id: id},
- ),
- returnValue: _i6.Future<_i5.PublicKey>.value(_FakePublicKey_39(
- this,
- Invocation.method(
- #getDeployKey,
- [slug],
- {#id: id},
- ),
- )),
- ) as _i6.Future<_i5.PublicKey>);
- @override
- _i6.Future<_i5.PublicKey> createDeployKey(
- _i5.RepositorySlug? slug,
- _i5.CreatePublicKey? key,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #createDeployKey,
- [
- slug,
- key,
- ],
- ),
- returnValue: _i6.Future<_i5.PublicKey>.value(_FakePublicKey_39(
- this,
- Invocation.method(
- #createDeployKey,
- [
- slug,
- key,
- ],
- ),
- )),
- ) as _i6.Future<_i5.PublicKey>);
- @override
- _i6.Future<bool> deleteDeployKey({
- required _i5.RepositorySlug? slug,
- required _i5.PublicKey? key,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #deleteDeployKey,
- [],
- {
- #slug: slug,
- #key: key,
- },
- ),
- returnValue: _i6.Future<bool>.value(false),
- ) as _i6.Future<bool>);
- @override
- _i6.Future<_i5.RepositoryCommit> merge(
- _i5.RepositorySlug? slug,
- _i5.CreateMerge? merge,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #merge,
- [
- slug,
- merge,
- ],
- ),
- returnValue: _i6.Future<_i5.RepositoryCommit>.value(_FakeRepositoryCommit_33(
- this,
- Invocation.method(
- #merge,
- [
- slug,
- merge,
- ],
- ),
- )),
- ) as _i6.Future<_i5.RepositoryCommit>);
- @override
- _i6.Future<_i5.RepositoryPages> getPagesInfo(_i5.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #getPagesInfo,
- [slug],
- ),
- returnValue: _i6.Future<_i5.RepositoryPages>.value(_FakeRepositoryPages_40(
- this,
- Invocation.method(
- #getPagesInfo,
- [slug],
- ),
- )),
- ) as _i6.Future<_i5.RepositoryPages>);
- @override
- _i6.Stream<_i5.PageBuild> listPagesBuilds(_i5.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #listPagesBuilds,
- [slug],
- ),
- returnValue: _i6.Stream<_i5.PageBuild>.empty(),
- ) as _i6.Stream<_i5.PageBuild>);
- @override
- _i6.Future<_i5.PageBuild> getLatestPagesBuild(_i5.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #getLatestPagesBuild,
- [slug],
- ),
- returnValue: _i6.Future<_i5.PageBuild>.value(_FakePageBuild_41(
- this,
- Invocation.method(
- #getLatestPagesBuild,
- [slug],
- ),
- )),
- ) as _i6.Future<_i5.PageBuild>);
- @override
- _i6.Stream<_i5.Release> listReleases(_i5.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #listReleases,
- [slug],
- ),
- returnValue: _i6.Stream<_i5.Release>.empty(),
- ) as _i6.Stream<_i5.Release>);
- @override
- _i6.Future<_i5.Release> getLatestRelease(_i5.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #getLatestRelease,
- [slug],
- ),
- returnValue: _i6.Future<_i5.Release>.value(_FakeRelease_42(
- this,
- Invocation.method(
- #getLatestRelease,
- [slug],
- ),
- )),
- ) as _i6.Future<_i5.Release>);
- @override
- _i6.Future<_i5.Release> getReleaseById(
- _i5.RepositorySlug? slug,
- int? id,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #getReleaseById,
- [
- slug,
- id,
- ],
- ),
- returnValue: _i6.Future<_i5.Release>.value(_FakeRelease_42(
- this,
- Invocation.method(
- #getReleaseById,
- [
- slug,
- id,
- ],
- ),
- )),
- ) as _i6.Future<_i5.Release>);
- @override
- _i6.Future<_i5.Release> getReleaseByTagName(
- _i5.RepositorySlug? slug,
- String? tagName,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #getReleaseByTagName,
- [
- slug,
- tagName,
- ],
- ),
- returnValue: _i6.Future<_i5.Release>.value(_FakeRelease_42(
- this,
- Invocation.method(
- #getReleaseByTagName,
- [
- slug,
- tagName,
- ],
- ),
- )),
- ) as _i6.Future<_i5.Release>);
- @override
- _i6.Future<_i5.Release> createRelease(
- _i5.RepositorySlug? slug,
- _i5.CreateRelease? createRelease, {
- bool? getIfExists = true,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #createRelease,
- [
- slug,
- createRelease,
- ],
- {#getIfExists: getIfExists},
- ),
- returnValue: _i6.Future<_i5.Release>.value(_FakeRelease_42(
- this,
- Invocation.method(
- #createRelease,
- [
- slug,
- createRelease,
- ],
- {#getIfExists: getIfExists},
- ),
- )),
- ) as _i6.Future<_i5.Release>);
- @override
- _i6.Future<_i5.Release> editRelease(
- _i5.RepositorySlug? slug,
- _i5.Release? releaseToEdit, {
- String? tagName,
- String? targetCommitish,
- String? name,
- String? body,
- bool? draft,
- bool? preRelease,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #editRelease,
- [
- slug,
- releaseToEdit,
- ],
- {
- #tagName: tagName,
- #targetCommitish: targetCommitish,
- #name: name,
- #body: body,
- #draft: draft,
- #preRelease: preRelease,
- },
- ),
- returnValue: _i6.Future<_i5.Release>.value(_FakeRelease_42(
- this,
- Invocation.method(
- #editRelease,
- [
- slug,
- releaseToEdit,
- ],
- {
- #tagName: tagName,
- #targetCommitish: targetCommitish,
- #name: name,
- #body: body,
- #draft: draft,
- #preRelease: preRelease,
- },
- ),
- )),
- ) as _i6.Future<_i5.Release>);
- @override
- _i6.Future<bool> deleteRelease(
- _i5.RepositorySlug? slug,
- _i5.Release? release,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #deleteRelease,
- [
- slug,
- release,
- ],
- ),
- returnValue: _i6.Future<bool>.value(false),
- ) as _i6.Future<bool>);
- @override
- _i6.Stream<_i5.ReleaseAsset> listReleaseAssets(
- _i5.RepositorySlug? slug,
- _i5.Release? release,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #listReleaseAssets,
- [
- slug,
- release,
- ],
- ),
- returnValue: _i6.Stream<_i5.ReleaseAsset>.empty(),
- ) as _i6.Stream<_i5.ReleaseAsset>);
- @override
- _i6.Future<_i5.ReleaseAsset> getReleaseAsset(
- _i5.RepositorySlug? slug,
- _i5.Release? release, {
- required int? assetId,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #getReleaseAsset,
- [
- slug,
- release,
- ],
- {#assetId: assetId},
- ),
- returnValue: _i6.Future<_i5.ReleaseAsset>.value(_FakeReleaseAsset_43(
- this,
- Invocation.method(
- #getReleaseAsset,
- [
- slug,
- release,
- ],
- {#assetId: assetId},
- ),
- )),
- ) as _i6.Future<_i5.ReleaseAsset>);
- @override
- _i6.Future<_i5.ReleaseAsset> editReleaseAsset(
- _i5.RepositorySlug? slug,
- _i5.ReleaseAsset? assetToEdit, {
- String? name,
- String? label,
- }) =>
- (super.noSuchMethod(
- Invocation.method(
- #editReleaseAsset,
- [
- slug,
- assetToEdit,
- ],
- {
- #name: name,
- #label: label,
- },
- ),
- returnValue: _i6.Future<_i5.ReleaseAsset>.value(_FakeReleaseAsset_43(
- this,
- Invocation.method(
- #editReleaseAsset,
- [
- slug,
- assetToEdit,
- ],
- {
- #name: name,
- #label: label,
- },
- ),
- )),
- ) as _i6.Future<_i5.ReleaseAsset>);
- @override
- _i6.Future<bool> deleteReleaseAsset(
- _i5.RepositorySlug? slug,
- _i5.ReleaseAsset? asset,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #deleteReleaseAsset,
- [
- slug,
- asset,
- ],
- ),
- returnValue: _i6.Future<bool>.value(false),
- ) as _i6.Future<bool>);
- @override
- _i6.Future<List<_i5.ReleaseAsset>> uploadReleaseAssets(
- _i5.Release? release,
- Iterable<_i5.CreateReleaseAsset>? createReleaseAssets,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #uploadReleaseAssets,
- [
- release,
- createReleaseAssets,
- ],
- ),
- returnValue: _i6.Future<List<_i5.ReleaseAsset>>.value(<_i5.ReleaseAsset>[]),
- ) as _i6.Future<List<_i5.ReleaseAsset>>);
- @override
- _i6.Future<List<_i5.ContributorStatistics>> listContributorStats(_i5.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #listContributorStats,
- [slug],
- ),
- returnValue: _i6.Future<List<_i5.ContributorStatistics>>.value(<_i5.ContributorStatistics>[]),
- ) as _i6.Future<List<_i5.ContributorStatistics>>);
- @override
- _i6.Stream<_i5.YearCommitCountWeek> listCommitActivity(_i5.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #listCommitActivity,
- [slug],
- ),
- returnValue: _i6.Stream<_i5.YearCommitCountWeek>.empty(),
- ) as _i6.Stream<_i5.YearCommitCountWeek>);
- @override
- _i6.Stream<_i5.WeeklyChangesCount> listCodeFrequency(_i5.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #listCodeFrequency,
- [slug],
- ),
- returnValue: _i6.Stream<_i5.WeeklyChangesCount>.empty(),
- ) as _i6.Stream<_i5.WeeklyChangesCount>);
- @override
- _i6.Future<_i5.ContributorParticipation> getParticipation(_i5.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #getParticipation,
- [slug],
- ),
- returnValue: _i6.Future<_i5.ContributorParticipation>.value(_FakeContributorParticipation_44(
- this,
- Invocation.method(
- #getParticipation,
- [slug],
- ),
- )),
- ) as _i6.Future<_i5.ContributorParticipation>);
- @override
- _i6.Stream<_i5.PunchcardEntry> listPunchcard(_i5.RepositorySlug? slug) => (super.noSuchMethod(
- Invocation.method(
- #listPunchcard,
- [slug],
- ),
- returnValue: _i6.Stream<_i5.PunchcardEntry>.empty(),
- ) as _i6.Stream<_i5.PunchcardEntry>);
- @override
- _i6.Stream<_i5.RepositoryStatus> listStatuses(
- _i5.RepositorySlug? slug,
- String? ref,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #listStatuses,
- [
- slug,
- ref,
- ],
- ),
- returnValue: _i6.Stream<_i5.RepositoryStatus>.empty(),
- ) as _i6.Stream<_i5.RepositoryStatus>);
- @override
- _i6.Future<_i5.RepositoryStatus> createStatus(
- _i5.RepositorySlug? slug,
- String? ref,
- _i5.CreateStatus? request,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #createStatus,
- [
- slug,
- ref,
- request,
- ],
- ),
- returnValue: _i6.Future<_i5.RepositoryStatus>.value(_FakeRepositoryStatus_45(
- this,
- Invocation.method(
- #createStatus,
- [
- slug,
- ref,
- request,
- ],
- ),
- )),
- ) as _i6.Future<_i5.RepositoryStatus>);
- @override
- _i6.Future<_i5.CombinedRepositoryStatus> getCombinedStatus(
- _i5.RepositorySlug? slug,
- String? ref,
- ) =>
- (super.noSuchMethod(
- Invocation.method(
- #getCombinedStatus,
- [
- slug,
- ref,
- ],
- ),
- returnValue: _i6.Future<_i5.CombinedRepositoryStatus>.value(_FakeCombinedRepositoryStatus_46(
- this,
- Invocation.method(
- #getCombinedStatus,
- [
- slug,
- ref,
- ],
- ),
- )),
- ) as _i6.Future<_i5.CombinedRepositoryStatus>);
- @override
- _i6.Future<_i5.ReleaseNotes> generateReleaseNotes(_i5.CreateReleaseNotes? crn) => (super.noSuchMethod(
- Invocation.method(
- #generateReleaseNotes,
- [crn],
- ),
- returnValue: _i6.Future<_i5.ReleaseNotes>.value(_FakeReleaseNotes_47(
- this,
- Invocation.method(
- #generateReleaseNotes,
- [crn],
- ),
- )),
- ) as _i6.Future<_i5.ReleaseNotes>);
-}
-
-/// A class which mocks [GitHubComparison].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockGitHubComparison extends _i1.Mock implements _i5.GitHubComparison {
- MockGitHubComparison() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- set url(String? _url) => super.noSuchMethod(
- Invocation.setter(
- #url,
- _url,
- ),
- returnValueForMissingStub: null,
- );
- @override
- set status(String? _status) => super.noSuchMethod(
- Invocation.setter(
- #status,
- _status,
- ),
- returnValueForMissingStub: null,
- );
- @override
- set aheadBy(int? _aheadBy) => super.noSuchMethod(
- Invocation.setter(
- #aheadBy,
- _aheadBy,
- ),
- returnValueForMissingStub: null,
- );
- @override
- set behindBy(int? _behindBy) => super.noSuchMethod(
- Invocation.setter(
- #behindBy,
- _behindBy,
- ),
- returnValueForMissingStub: null,
- );
- @override
- set totalCommits(int? _totalCommits) => super.noSuchMethod(
- Invocation.setter(
- #totalCommits,
- _totalCommits,
- ),
- returnValueForMissingStub: null,
- );
- @override
- set files(List<_i5.CommitFile>? _files) => super.noSuchMethod(
- Invocation.setter(
- #files,
- _files,
- ),
- returnValueForMissingStub: null,
- );
- @override
- set commits(List<_i5.RepositoryCommit>? _commits) => super.noSuchMethod(
- Invocation.setter(
- #commits,
- _commits,
- ),
- returnValueForMissingStub: null,
- );
- @override
- Map<String, dynamic> toJson() => (super.noSuchMethod(
- Invocation.method(
- #toJson,
- [],
- ),
- returnValue: <String, dynamic>{},
- ) as Map<String, dynamic>);
-}
-
-/// A class which mocks [Response].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockResponse extends _i1.Mock implements _i2.Response {
- MockResponse() {
- _i1.throwOnMissingStub(this);
- }
-
- @override
- _i11.Uint8List get bodyBytes => (super.noSuchMethod(
- Invocation.getter(#bodyBytes),
- returnValue: _i11.Uint8List(0),
- ) as _i11.Uint8List);
- @override
- String get body => (super.noSuchMethod(
- Invocation.getter(#body),
- returnValue: '',
- ) as String);
- @override
- int get statusCode => (super.noSuchMethod(
- Invocation.getter(#statusCode),
- returnValue: 0,
- ) as int);
- @override
- Map<String, String> get headers => (super.noSuchMethod(
- Invocation.getter(#headers),
- returnValue: <String, String>{},
- ) as Map<String, String>);
- @override
- bool get isRedirect => (super.noSuchMethod(
- Invocation.getter(#isRedirect),
- returnValue: false,
- ) as bool);
- @override
- bool get persistentConnection => (super.noSuchMethod(
- Invocation.getter(#persistentConnection),
- returnValue: false,
- ) as bool);
-}
diff --git a/auto_submit/test/utilities/utils.dart b/auto_submit/test/utilities/utils.dart
deleted file mode 100644
index 55e0cf8..0000000
--- a/auto_submit/test/utilities/utils.dart
+++ /dev/null
@@ -1,137 +0,0 @@
-// Copyright 2022 The Flutter 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 'package:meta/meta.dart';
-import 'package:github/github.dart';
-import 'package:auto_submit/model/auto_submit_query_result.dart';
-
-const String oid = '6dcb09b5b57875f334f61aebed695e2e4193db5e';
-const String title = 'some_title';
-
-/// Helper for Github statuses.
-@immutable
-class StatusHelper {
- const StatusHelper(this.name, this.state);
-
- static const StatusHelper flutterBuildSuccess = StatusHelper('tree-status', 'SUCCESS');
- static const StatusHelper flutterBuildFailure = StatusHelper('tree-status', 'FAILURE');
- static const StatusHelper otherStatusFailure = StatusHelper('other status', 'FAILURE');
-
- final String name;
- final String state;
-}
-
-/// Helper to generate Github pull requests.
-class PullRequestHelper {
- PullRequestHelper({
- this.author = 'author1',
- this.prNumber = 0,
- this.repo = 'flutter',
- this.authorAssociation = 'MEMBER',
- this.title = 'some_title',
- this.mergeableState = MergeableState.MERGEABLE,
- this.reviews = const <PullRequestReviewHelper>[
- PullRequestReviewHelper(authorName: 'member', state: ReviewState.APPROVED, memberType: MemberType.MEMBER),
- ],
- this.lastCommitHash = 'oid',
- this.lastCommitStatuses = const <StatusHelper>[StatusHelper.flutterBuildSuccess],
- this.lastCommitMessage = '',
- this.dateTime,
- this.mergedAt,
- });
-
- final int prNumber;
- final String repo;
- final String author;
- final String authorAssociation;
- final List<PullRequestReviewHelper> reviews;
- final String lastCommitHash;
- List<StatusHelper>? lastCommitStatuses;
- final String? lastCommitMessage;
- final DateTime? dateTime;
- final String title;
- final MergeableState mergeableState;
- final DateTime? mergedAt;
-
- RepositorySlug get slug => RepositorySlug('flutter', repo);
-
- Map<String, dynamic> toEntry() {
- return <String, dynamic>{
- 'author': <String, dynamic>{'login': author},
- 'authorAssociation': authorAssociation,
- 'id': prNumber.toString(),
- 'number': prNumber,
- 'title': title,
- 'mergeable': mergeableState.name,
- 'mergedAt': (mergedAt ?? DateTime.now().subtract(const Duration(hours: 12))).toUtc().toIso8601String(),
- 'reviews': <String, dynamic>{
- 'nodes': reviews.map((PullRequestReviewHelper review) {
- return <String, dynamic>{
- 'author': <String, dynamic>{'login': review.authorName},
- 'authorAssociation': review.memberType.toString().replaceFirst('MemberType.', ''),
- 'state': review.state.toString().replaceFirst('ReviewState.', ''),
- };
- }).toList(),
- },
- 'commits': <String, dynamic>{
- 'nodes': <dynamic>[
- <String, dynamic>{
- 'commit': <String, dynamic>{
- 'oid': lastCommitHash,
- 'pushedDate': (dateTime ?? DateTime.now().add(const Duration(hours: -2))).toUtc().toIso8601String(),
- 'message': lastCommitMessage,
- 'status': <String, dynamic>{
- 'contexts': lastCommitStatuses != null
- ? lastCommitStatuses!.map((StatusHelper status) {
- return <String, dynamic>{
- 'context': status.name,
- 'state': status.state,
- 'targetUrl': 'https://${status.name}',
- };
- }).toList()
- : <dynamic>[],
- },
- },
- }
- ],
- },
- };
- }
-}
-
-/// Generate `QueryResult` model as in auto submit.
-QueryResult createQueryResult(PullRequestHelper pullRequest) {
- return QueryResult.fromJson(<String, dynamic>{
- 'repository': <String, dynamic>{
- 'pullRequest': pullRequest.toEntry().cast<String, dynamic>(),
- },
- });
-}
-
-/// List of review state from a github pull request.
-enum ReviewState {
- APPROVED,
- CHANGES_REQUESTED,
-}
-
-/// List of member type of a github pull request author/reviewer.
-enum MemberType {
- OWNER,
- MEMBER,
- OTHER,
-}
-
-/// Details of a github pull request review.
-@immutable
-class PullRequestReviewHelper {
- const PullRequestReviewHelper({
- required this.authorName,
- required this.state,
- required this.memberType,
- });
-
- final String authorName;
- final ReviewState state;
- final MemberType memberType;
-}
diff --git a/auto_submit/test/validations/approval_test.dart b/auto_submit/test/validations/approval_test.dart
deleted file mode 100644
index 71884fa..0000000
--- a/auto_submit/test/validations/approval_test.dart
+++ /dev/null
@@ -1,297 +0,0 @@
-// Copyright 2023 The Flutter 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:convert';
-
-import 'package:auto_submit/configuration/repository_configuration.dart';
-import 'package:auto_submit/model/auto_submit_query_result.dart';
-import 'package:auto_submit/validations/approval.dart';
-import 'package:auto_submit/validations/validation.dart';
-import 'package:test/test.dart';
-
-import 'package:github/github.dart' as gh;
-import '../configuration/repository_configuration_data.dart';
-import '../requests/github_webhook_test_data.dart';
-import '../src/service/fake_config.dart';
-import '../src/service/fake_github_service.dart';
-import '../src/service/fake_graphql_client.dart';
-import '../utilities/mocks.mocks.dart';
-import 'approval_test_data.dart';
-
-void main() {
- late Approval approval;
- late FakeConfig config;
- final FakeGithubService githubService = FakeGithubService();
- late FakeGraphQLClient githubGraphQLClient;
- final MockGitHub gitHub = MockGitHub();
-
- setUp(() {
- githubGraphQLClient = FakeGraphQLClient();
- config = FakeConfig(githubService: githubService, githubGraphQLClient: githubGraphQLClient, githubClient: gitHub);
- config.repositoryConfigurationMock = RepositoryConfiguration.fromYaml(sampleConfigNoOverride);
- approval = Approval(config: config);
- });
-
- group('Approval group tests', () {
- Future<ValidationResult> computeValidationResult(String review) async {
- final Map<String, dynamic> queryResultJsonDecode = jsonDecode(review) as Map<String, dynamic>;
- final QueryResult queryResult = QueryResult.fromJson(queryResultJsonDecode);
- final gh.PullRequest pullRequest = generatePullRequest();
- return approval.validate(queryResult, pullRequest);
- }
-
- test('Author and reviewer in flutter-hackers, pr approved', () async {
- final String review = constructSingleReviewerReview(
- reviewState: 'APPROVED',
- );
-
- // githubService.isTeamMemberMockList = [true, true];
- githubService.isTeamMemberMockMap['author1'] = true;
- githubService.isTeamMemberMockMap['keyonghan'] = true;
- final ValidationResult result = await computeValidationResult(review);
-
- expect(result.result, isTrue);
- expect(result.action, Action.REMOVE_LABEL);
- expect(result.message.contains('This PR has met approval requirements for merging.'), isTrue);
- });
-
- test('Author is a NON member and reviewer is a member, need 1 more review', () async {
- // githubService.isTeamMemberMockList = [true, true];
- githubService.isTeamMemberMockMap['author1'] = false;
- githubService.isTeamMemberMockMap['keyonghan'] = true;
- final String review = constructSingleReviewerReview(
- reviewState: 'APPROVED',
- );
-
- final ValidationResult result = await computeValidationResult(review);
-
- expect(result.result, isFalse);
- expect(result.action, Action.REMOVE_LABEL);
- expect(result.message.contains('This PR has not met approval requirements for merging.'), isTrue);
- expect(result.message.contains('need 1 more review'), isTrue);
- });
-
- test('Author is a NON member and reviewer is a NON member, need 2 more reviews', () async {
- // githubService.isTeamMemberMockList = [true, true];
- githubService.isTeamMemberMockMap['author1'] = false;
- githubService.isTeamMemberMockMap['keyonghan'] = false;
- final String review = constructSingleReviewerReview(
- reviewState: 'APPROVED',
- );
-
- final ValidationResult result = await computeValidationResult(review);
-
- expect(result.result, isFalse);
- expect(result.action, Action.REMOVE_LABEL);
- expect(result.message.contains('This PR has not met approval requirements for merging.'), isTrue);
- expect(result.message.contains('need 2 more review'), isTrue);
- });
-
- test('Author is a member and reviewer is NON member, need 1 more review', () async {
- // githubService.isTeamMemberMockList = [true, true];
- githubService.isTeamMemberMockMap['author1'] = true;
- githubService.isTeamMemberMockMap['keyonghan'] = false;
- final String review = constructSingleReviewerReview(
- reviewState: 'APPROVED',
- );
-
- final ValidationResult result = await computeValidationResult(review);
-
- expect(result.result, isFalse);
- expect(result.action, Action.IGNORE_TEMPORARILY);
- expect(result.message.contains('This PR has not met approval requirements for merging.'), isTrue);
- expect(result.message.contains('need 1 more review'), isTrue);
- });
-
- test('Author is NON member and reviewers are members, pr approved', () async {
- githubService.isTeamMemberMockMap['author1'] = false;
- githubService.isTeamMemberMockMap['author2'] = true;
- githubService.isTeamMemberMockMap['author3'] = true;
- final String review = constructTwoReviewerReview(
- reviewState: 'APPROVED',
- secondReviewState: 'APPROVED',
- );
-
- final ValidationResult result = await computeValidationResult(review);
-
- expect(result.result, isTrue);
- expect(result.action, Action.REMOVE_LABEL);
- expect(result.message.contains('This PR has met approval requirements for merging.'), isTrue);
- });
-
- test('Author is NON member and one reviewer is a NON member, need 1 more review', () async {
- githubService.isTeamMemberMockMap['author1'] = false;
- githubService.isTeamMemberMockMap['author2'] = true;
- githubService.isTeamMemberMockMap['author3'] = false;
- final String review = constructTwoReviewerReview(
- reviewState: 'APPROVED',
- secondReviewState: 'APPROVED',
- );
-
- final ValidationResult result = await computeValidationResult(review);
-
- expect(result.result, isFalse);
- expect(result.action, Action.REMOVE_LABEL);
- expect(result.message.contains('This PR has not met approval requirements for merging.'), isTrue);
- expect(result.message.contains('need 1 more review'), isTrue);
- });
-
- test('Author is member and reviewers are NON members, need 1 more review', () async {
- githubService.isTeamMemberMockMap['author1'] = true;
- githubService.isTeamMemberMockMap['author2'] = false;
- githubService.isTeamMemberMockMap['author3'] = false;
- final String review = constructTwoReviewerReview(
- reviewState: 'APPROVED',
- secondReviewState: 'APPROVED',
- );
-
- final ValidationResult result = await computeValidationResult(review);
-
- expect(result.result, isFalse);
- expect(result.action, Action.IGNORE_TEMPORARILY);
- expect(result.message.contains('This PR has not met approval requirements for merging.'), isTrue);
- expect(result.message.contains('need 1 more review'), isTrue);
- });
-
- test('Author is NON member and reviewers are NON members, need 2 reviews', () async {
- githubService.isTeamMemberMockMap['author1'] = false;
- githubService.isTeamMemberMockMap['author2'] = false;
- githubService.isTeamMemberMockMap['author3'] = false;
- final String review = constructTwoReviewerReview(
- reviewState: 'APPROVED',
- secondReviewState: 'APPROVED',
- );
-
- final ValidationResult result = await computeValidationResult(review);
-
- expect(result.result, isFalse);
- expect(result.action, Action.REMOVE_LABEL);
- expect(result.message.contains('This PR has not met approval requirements for merging.'), isTrue);
- expect(result.message.contains('need 2 more review'), isTrue);
- });
-
- test('Verify author review count does not go negative', () async {
- githubService.isTeamMemberMockMap['author1'] = false;
- githubService.isTeamMemberMockMap['ricardoamador'] = false;
- githubService.isTeamMemberMockMap['keyonghan'] = false;
- githubService.isTeamMemberMockMap['nehalvpatel'] = false;
- final String review = constructMultipleReviewerReview(
- reviewState: 'APPROVED',
- secondReviewState: 'APPROVED',
- thirdReviewState: 'APPROVED',
- );
-
- final ValidationResult result = await computeValidationResult(review);
-
- expect(result.result, isFalse);
- expect(result.action, Action.REMOVE_LABEL);
- expect(result.message.contains('This PR has not met approval requirements for merging.'), isTrue);
- expect(result.message.contains('need 2 more review'), isTrue);
- });
-
- test('Verify author review count does not go negative', () async {
- githubService.isTeamMemberMockMap['author1'] = true;
- githubService.isTeamMemberMockMap['ricardoamador'] = true;
- githubService.isTeamMemberMockMap['keyonghan'] = true;
- githubService.isTeamMemberMockMap['nehalvpatel'] = true;
- final String review = constructMultipleReviewerReview(
- reviewState: 'APPROVED',
- secondReviewState: 'APPROVED',
- thirdReviewState: 'APPROVED',
- );
-
- final ValidationResult result = await computeValidationResult(review);
-
- expect(result.result, isTrue);
- expect(result.action, Action.REMOVE_LABEL);
- expect(result.message.contains('This PR has met approval requirements for merging.'), isTrue);
- });
-
- test('Author is member and member requests changes, 1 review is needed', () async {
- githubService.isTeamMemberMockMap['author1'] = true;
- githubService.isTeamMemberMockMap['keyonghan'] = true;
- final String review = constructSingleReviewerReview(
- reviewState: 'CHANGES_REQUESTED',
- );
-
- final ValidationResult result = await computeValidationResult(review);
-
- expect(result.result, isFalse);
- expect(result.action, Action.REMOVE_LABEL);
- expect(result.message.contains('This PR has not met approval requirements for merging.'), isTrue);
- expect(result.message.contains('Changes were requested by'), isTrue);
- });
-
- test('Author is member and two member reviews, 1 change request, review is not approved', () async {
- githubService.isTeamMemberMockMap['author1'] = true;
- githubService.isTeamMemberMockMap['author2'] = true;
- githubService.isTeamMemberMockMap['author3'] = true;
- final String review = constructTwoReviewerReview(
- reviewState: 'CHANGES_REQUESTED',
- secondReviewState: 'APPROVED',
- );
-
- final ValidationResult result = await computeValidationResult(review);
-
- expect(result.result, isFalse);
- expect(result.action, Action.REMOVE_LABEL);
- expect(result.message.contains('This PR has not met approval requirements for merging.'), isTrue);
- expect(result.message.contains('Changes were requested by'), isTrue);
- });
-
- test('Multiple approving reviews from the same author are counted only 1 time.', () async {
- githubService.isTeamMemberMockMap['author1'] = false;
- githubService.isTeamMemberMockMap['ricardoamador'] = true;
- final String review = constructTwoReviewerReview(
- reviewState: 'APPROVED',
- secondReviewState: 'APPROVED',
- author: 'ricardoamador',
- secondAuthor: 'ricardoamador',
- );
-
- final ValidationResult result = await computeValidationResult(review);
-
- expect(result.result, isFalse);
- expect(result.action, Action.REMOVE_LABEL);
- expect(
- result.message.contains(
- 'This PR has not met approval requirements for merging. You are not a member of flutter-hackers and need 1 more review(s) in order to merge this PR.',
- ),
- isTrue,
- );
- });
-
- test('Successful review overwrites previous changes requested.', () async {
- githubService.isTeamMemberMockMap['author1'] = true;
- githubService.isTeamMemberMockMap['keyonghan'] = true;
- githubService.isTeamMemberMockMap['jmagman'] = true;
- final ValidationResult result = await computeValidationResult(multipleReviewsSameAuthor);
-
- expect(result.result, isTrue);
- expect(result.action, Action.REMOVE_LABEL);
- expect(result.message.contains('This PR has met approval requirements for merging.'), isTrue);
- });
-
- test('Author cannot review own pr', () async {
- githubService.isTeamMemberMockMap['author1'] = false;
- githubService.isTeamMemberMockMap['author3'] = true;
- final String review = constructTwoReviewerReview(
- reviewState: 'APPROVED',
- secondReviewState: 'APPROVED',
- author: 'author1',
- );
-
- final ValidationResult result = await computeValidationResult(review);
-
- expect(result.result, isFalse);
- expect(result.action, Action.REMOVE_LABEL);
- expect(
- result.message.contains(
- 'This PR has not met approval requirements for merging. You are not a member of flutter-hackers',
- ),
- isTrue,
- );
- });
- });
-}
diff --git a/auto_submit/test/validations/approval_test_data.dart b/auto_submit/test/validations/approval_test_data.dart
deleted file mode 100644
index b03ca3a..0000000
--- a/auto_submit/test/validations/approval_test_data.dart
+++ /dev/null
@@ -1,242 +0,0 @@
-// Copyright 2023 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-String constructSingleReviewerReview({
- required String reviewState,
-}) {
- return '''
- {
- "repository": {
- "pullRequest": {
- "author": {
- "login": "author1"
- },
- "id": "PR_kwDOA8VHis43rs4_",
- "title": "[dependabot] Remove human reviewers",
- "commits": {
- "nodes":[
- {
- "commit": {
- "abbreviatedOid": "4009ecc",
- "oid": "4009ecc0b6dbf5cb19cb97472147063e7368ec10",
- "committedDate": "2022-05-11T22:35:02Z",
- "pushedDate": "2022-05-11T22:35:03Z",
- "status": {
- "contexts":[
- {
- "context":"tree-status",
- "state":"SUCCESS",
- "targetUrl":"https://ci.example.com/1000/output"
- }
- ]
- }
- }
- }
- ]
- },
- "reviews": {
- "nodes": [
- {
- "author": {
- "login": "keyonghan"
- },
- "state": "$reviewState"
- }
- ]
- }
- }
- }
- }
- ''';
-}
-
-String constructTwoReviewerReview({
- required String reviewState,
- required String secondReviewState,
- String author = 'author2',
- String secondAuthor = 'author3',
-}) {
- return '''
- {
- "repository": {
- "pullRequest": {
- "author": {
- "login": "author1"
- },
- "id": "PR_kwDOA8VHis43rs4_",
- "title": "[dependabot] Remove human reviewers",
- "commits": {
- "nodes":[
- {
- "commit": {
- "abbreviatedOid": "4009ecc",
- "oid": "4009ecc0b6dbf5cb19cb97472147063e7368ec10",
- "committedDate": "2022-05-11T22:35:02Z",
- "pushedDate": "2022-05-11T22:35:03Z",
- "status": {
- "contexts":[
- {
- "context":"tree-status",
- "state":"SUCCESS",
- "targetUrl":"https://ci.example.com/1000/output"
- }
- ]
- }
- }
- }
- ]
- },
- "reviews": {
- "nodes": [
- {
- "author": {
- "login": "$author"
- },
- "state": "$reviewState"
- },
- {
- "author": {
- "login": "$secondAuthor"
- },
- "state": "$secondReviewState"
- }
- ]
- }
- }
- }
- }
- ''';
-}
-
-String constructMultipleReviewerReview({
- required String reviewState,
- required String secondReviewState,
- required String thirdReviewState,
-}) {
- return '''
- {
- "repository": {
- "pullRequest": {
- "author": {
- "login": "author1"
- },
- "id": "PR_kwDOA8VHis43rs4_",
- "title": "[dependabot] Remove human reviewers",
- "commits": {
- "nodes":[
- {
- "commit": {
- "abbreviatedOid": "4009ecc",
- "oid": "4009ecc0b6dbf5cb19cb97472147063e7368ec10",
- "committedDate": "2022-05-11T22:35:02Z",
- "pushedDate": "2022-05-11T22:35:03Z",
- "status": {
- "contexts":[
- {
- "context":"tree-status",
- "state":"SUCCESS",
- "targetUrl":"https://ci.example.com/1000/output"
- }
- ]
- }
- }
- }
- ]
- },
- "reviews": {
- "nodes": [
- {
- "author": {
- "login": "keyonghan"
- },
- "state": "$reviewState"
- },
- {
- "author": {
- "login": "ricardoamador"
- },
- "state": "$secondReviewState"
- },
- {
- "author": {
- "login": "nehalvpatel"
- },
- "state": "$thirdReviewState"
- }
- ]
- }
- }
- }
- }
- ''';
-}
-
-const String multipleReviewsSameAuthor = '''
-{
- "repository": {
- "pullRequest": {
- "author": {
- "login": "author1"
- },
- "id": "PR_kwDOA8VHis43rs4_",
- "title": "[dependabot] Remove human reviewers",
- "commits": {
- "nodes":[
- {
- "commit": {
- "abbreviatedOid": "4009ecc",
- "oid": "4009ecc0b6dbf5cb19cb97472147063e7368ec10",
- "committedDate": "2022-05-11T22:35:02Z",
- "pushedDate": "2022-05-11T22:35:03Z",
- "status": {
- "contexts":[
- {
- "context":"tree-status",
- "state":"SUCCESS",
- "targetUrl":"https://ci.example.com/1000/output"
- }
- ]
- }
- }
- }
- ]
- },
- "reviews": {
- "nodes": [
- {
- "author": {
- "login": "jmagman"
- },
- "state": "COMMENTED"
- },
- {
- "author": {
- "login": "keyonghan"
- },
- "state": "COMMENTED"
- },
- {
- "author": {
- "login": "jmagman"
- },
- "state": "APPROVED"
- },
- {
- "author": {
- "login": "jmagman"
- },
- "state": "CHANGES_REQUESTED"
- },
- {
- "author": {
- "login": "jmagman"
- },
- "state": "APPROVED"
- }
- ]
- }
- }
- }
- }
-''';
diff --git a/auto_submit/test/validations/ci_successful_test.dart b/auto_submit/test/validations/ci_successful_test.dart
deleted file mode 100644
index 87f5d65..0000000
--- a/auto_submit/test/validations/ci_successful_test.dart
+++ /dev/null
@@ -1,515 +0,0 @@
-// Copyright 2022 The Flutter 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:convert';
-
-import 'ci_successful_test_data.dart';
-
-import 'package:github/github.dart' as github;
-import 'package:test/test.dart';
-import 'package:auto_submit/validations/ci_successful.dart';
-import 'package:auto_submit/model/auto_submit_query_result.dart';
-import 'package:auto_submit/validations/validation.dart';
-import 'package:auto_submit/configuration/repository_configuration.dart';
-
-import '../utilities/utils.dart';
-import '../utilities/mocks.dart';
-import '../src/service/fake_config.dart';
-import '../src/service/fake_github_service.dart';
-import '../src/service/fake_graphql_client.dart';
-import '../requests/github_webhook_test_data.dart';
-import '../configuration/repository_configuration_data.dart';
-
-void main() {
- late CiSuccessful ciSuccessful;
- late FakeConfig config;
- FakeGithubService githubService = FakeGithubService();
- late FakeGraphQLClient githubGraphQLClient;
- final MockGitHub gitHub = MockGitHub();
- late github.RepositorySlug slug;
- late Set<FailureDetail> failures;
- const int prNumber = 1;
-
- List<ContextNode> getContextNodeListFromJson(final String repositoryStatuses) {
- final List<ContextNode> contextNodeList = [];
-
- final Map<String, dynamic> contextNodeMap = jsonDecode(repositoryStatuses) as Map<String, dynamic>;
-
- final dynamic statuses = contextNodeMap['statuses'];
- for (Map<String, dynamic> map in statuses) {
- contextNodeList.add(ContextNode.fromJson(map));
- }
-
- return contextNodeList;
- }
-
- void convertContextNodeStatuses(List<ContextNode> contextNodeList) {
- for (ContextNode contextNode in contextNodeList) {
- contextNode.state = contextNode.state!.toUpperCase();
- }
- }
-
- /// Setup objects needed across test groups.
- setUp(() {
- githubGraphQLClient = FakeGraphQLClient();
- config = FakeConfig(githubService: githubService, githubGraphQLClient: githubGraphQLClient, githubClient: gitHub);
- ciSuccessful = CiSuccessful(config: config);
- slug = github.RepositorySlug('octocat', 'flutter');
- failures = <FailureDetail>{};
- config.repositoryConfigurationMock = RepositoryConfiguration.fromYaml(sampleConfigNoOverride);
- });
-
- group('validateCheckRuns', () {
- test('ValidateCheckRuns no failures for skipped conclusion.', () {
- githubService.checkRunsData = skippedCheckRunsMock;
- final Future<List<github.CheckRun>> checkRunFuture = githubService.getCheckRuns(slug, 'ref');
- const bool allSuccess = true;
-
- checkRunFuture.then((checkRuns) {
- expect(ciSuccessful.validateCheckRuns(slug, prNumber, checkRuns, failures, allSuccess), isTrue);
- expect(failures, isEmpty);
- });
- });
-
- test('ValidateCheckRuns no failures for successful conclusion.', () {
- githubService.checkRunsData = checkRunsMock;
- final Future<List<github.CheckRun>> checkRunFuture = githubService.getCheckRuns(slug, 'ref');
- const bool allSuccess = true;
-
- checkRunFuture.then((checkRuns) {
- expect(ciSuccessful.validateCheckRuns(slug, prNumber, checkRuns, failures, allSuccess), isTrue);
- expect(failures, isEmpty);
- });
- });
-
- test('ValidateCheckRuns no failure for status completed and neutral conclusion.', () {
- githubService.checkRunsData = neutralCheckRunsMock;
- final Future<List<github.CheckRun>> checkRunFuture = githubService.getCheckRuns(slug, 'ref');
- const bool allSuccess = true;
-
- checkRunFuture.then((checkRuns) {
- expect(ciSuccessful.validateCheckRuns(slug, prNumber, checkRuns, failures, allSuccess), isTrue);
- expect(failures, isEmpty);
- });
- });
-
- test('ValidateCheckRuns failure detected on status completed no neutral conclusion.', () {
- githubService.checkRunsData = failedCheckRunsMock;
- final Future<List<github.CheckRun>> checkRunFuture = githubService.getCheckRuns(slug, 'ref');
- const bool allSuccess = true;
-
- checkRunFuture.then((checkRuns) {
- expect(ciSuccessful.validateCheckRuns(slug, prNumber, checkRuns, failures, allSuccess), isFalse);
- expect(failures, isNotEmpty);
- expect(failures.length, 1);
- });
- });
-
- test('ValidateCheckRuns succes with multiple successful check runs.', () {
- githubService.checkRunsData = multipleCheckRunsMock;
- final Future<List<github.CheckRun>> checkRunFuture = githubService.getCheckRuns(slug, 'ref');
- const bool allSuccess = true;
-
- checkRunFuture.then((checkRuns) {
- expect(ciSuccessful.validateCheckRuns(slug, prNumber, checkRuns, failures, allSuccess), isTrue);
- expect(failures, isEmpty);
- });
- });
-
- test('ValidateCheckRuns failed with multiple check runs.', () {
- githubService.checkRunsData = multipleCheckRunsWithFailureMock;
- final Future<List<github.CheckRun>> checkRunFuture = githubService.getCheckRuns(slug, 'ref');
- const bool allSuccess = true;
-
- checkRunFuture.then((checkRuns) {
- expect(ciSuccessful.validateCheckRuns(slug, prNumber, checkRuns, failures, allSuccess), isFalse);
- expect(failures, isNotEmpty);
- expect(failures.length, 1);
- });
- });
-
- test('ValidateCheckRuns allSucces false but no failures recorded.', () {
- /// This test just checks that a checkRun that has not yet completed and
- /// does not cause failure is a candidate to be temporarily ignored.
- githubService.checkRunsData = inprogressAndNotFailedCheckRunMock;
- final Future<List<github.CheckRun>> checkRunFuture = githubService.getCheckRuns(slug, 'ref');
- const bool allSuccess = true;
-
- checkRunFuture.then((checkRuns) {
- expect(ciSuccessful.validateCheckRuns(slug, prNumber, checkRuns, failures, allSuccess), isFalse);
- expect(failures, isEmpty);
- });
- });
-
- test('ValidateCheckRuns allSuccess false is preserved.', () {
- githubService.checkRunsData = multipleCheckRunsWithFailureMock;
- final Future<List<github.CheckRun>> checkRunFuture = githubService.getCheckRuns(slug, 'ref');
- const bool allSuccess = false;
-
- checkRunFuture.then((checkRuns) {
- expect(ciSuccessful.validateCheckRuns(slug, prNumber, checkRuns, failures, allSuccess), isFalse);
- expect(failures, isNotEmpty);
- expect(failures.length, 1);
- });
- });
- });
-
- group('validateStatuses', () {
- test('Validate successful statuses show as successful.', () {
- final List<ContextNode> contextNodeList = getContextNodeListFromJson(repositoryStatusesMock);
- const bool allSuccess = true;
- final Author author = Author(login: 'ricardoamador');
-
- /// The status must be uppercase as the original code is expecting this.
- convertContextNodeStatuses(contextNodeList);
- expect(ciSuccessful.validateStatuses(slug, prNumber, author, [], contextNodeList, failures, allSuccess), isTrue);
- expect(failures, isEmpty);
- });
-
- test('Validate statuses that are not successful but do not cause failure.', () {
- final List<ContextNode> contextNodeList = getContextNodeListFromJson(failedAuthorsStatusesMock);
- const bool allSuccess = true;
- final Author author = Author(login: 'ricardoamador');
-
- final List<String> labelNames = [];
- labelNames.add('warning: land on red to fix tree breakage');
- labelNames.add('Other label');
-
- convertContextNodeStatuses(contextNodeList);
- expect(
- ciSuccessful.validateStatuses(slug, prNumber, author, labelNames, contextNodeList, failures, allSuccess),
- isTrue,
- );
- expect(failures, isEmpty);
- });
-
- test('Validate failure statuses do not cause failure with not in authors control.', () {
- final List<ContextNode> contextNodeList = getContextNodeListFromJson(failedAuthorsStatusesMock);
- const bool allSuccess = true;
- final Author author = Author(login: 'ricardoamador');
-
- final List<String> labelNames = [];
- labelNames.add('Compelling label');
- labelNames.add('Another Compelling label');
-
- convertContextNodeStatuses(contextNodeList);
- expect(
- ciSuccessful.validateStatuses(slug, prNumber, author, labelNames, contextNodeList, failures, allSuccess),
- isFalse,
- );
- expect(failures, isEmpty);
- });
-
- test('Validate failure statuses cause failures with not in authors control.', () {
- final List<ContextNode> contextNodeList = getContextNodeListFromJson(failedNonAuthorsStatusesMock);
- const bool allSuccess = true;
- final Author author = Author(login: 'ricardoamador');
-
- final List<String> labelNames = [];
- labelNames.add('Compelling label');
- labelNames.add('Another Compelling label');
-
- convertContextNodeStatuses(contextNodeList);
- expect(
- ciSuccessful.validateStatuses(slug, prNumber, author, labelNames, contextNodeList, failures, allSuccess),
- isFalse,
- );
- expect(failures, isNotEmpty);
- expect(failures.length, 2);
- });
-
- test('Validate failure statuses cause failures and preserves false allSuccess.', () {
- final List<ContextNode> contextNodeList = getContextNodeListFromJson(failedNonAuthorsStatusesMock);
- const bool allSuccess = false;
- final Author author = Author(login: 'ricardoamador');
-
- final List<String> labelNames = [];
- labelNames.add('Compelling label');
- labelNames.add('Another Compelling label');
-
- convertContextNodeStatuses(contextNodeList);
- expect(
- ciSuccessful.validateStatuses(slug, prNumber, author, labelNames, contextNodeList, failures, allSuccess),
- isFalse,
- );
- expect(failures, isNotEmpty);
- expect(failures.length, 2);
- });
-
- test('Validate flutter-gold is not checked for engine auto roller pull requests.', () {
- final List<ContextNode> contextNodeList = getContextNodeListFromJson(repositoryStatusesWithGoldMock);
- const bool allSuccess = true;
- final Author author = Author(login: 'skia-flutter-autoroll');
- slug = github.RepositorySlug('flutter', 'engine');
-
- final List<String> labelNames = [];
- labelNames.add('Compelling label');
- labelNames.add('Another Compelling label');
-
- convertContextNodeStatuses(contextNodeList);
- expect(
- ciSuccessful.validateStatuses(slug, prNumber, author, labelNames, contextNodeList, failures, allSuccess),
- isTrue,
- );
- expect(failures, isEmpty);
- expect(failures.length, 0);
- });
-
- test('Validate flutter-gold is not checked even if failing for engine auto roller pull requests.', () {
- final List<ContextNode> contextNodeList = getContextNodeListFromJson(repositoryStatusesWithFailedGoldMock);
- const bool allSuccess = true;
- final Author author = Author(login: 'skia-flutter-autoroll');
- slug = github.RepositorySlug('flutter', 'engine');
-
- final List<String> labelNames = [];
- labelNames.add('Compelling label');
- labelNames.add('Another Compelling label');
-
- convertContextNodeStatuses(contextNodeList);
- expect(
- ciSuccessful.validateStatuses(slug, prNumber, author, labelNames, contextNodeList, failures, allSuccess),
- isTrue,
- );
- expect(failures, isEmpty);
- expect(failures.length, 0);
- });
-
- test('Validate flutter-gold is checked for non engine auto roller pull requests.', () {
- final List<ContextNode> contextNodeList = getContextNodeListFromJson(repositoryStatusesWithFailedGoldMock);
- const bool allSuccess = true;
- final Author author = Author(login: 'ricardoamador');
- slug = github.RepositorySlug('flutter', 'engine');
-
- final List<String> labelNames = [];
- labelNames.add('Compelling label');
- labelNames.add('Another Compelling label');
-
- convertContextNodeStatuses(contextNodeList);
- expect(
- ciSuccessful.validateStatuses(slug, prNumber, author, labelNames, contextNodeList, failures, allSuccess),
- isFalse,
- );
- expect(failures, isNotEmpty);
- expect(failures.length, 1);
- });
- });
-
- group('treeStatusCheck', () {
- test('Validate tree status is set contains slug.', () {
- slug = github.RepositorySlug('flutter', 'flutter');
- final List<ContextNode> contextNodeList = getContextNodeListFromJson(repositoryStatusesMock);
- expect(contextNodeList.isEmpty, false);
-
- /// The status must be uppercase as the original code is expecting this.
- convertContextNodeStatuses(contextNodeList);
- final bool treeStatusFlag = ciSuccessful.treeStatusCheck(slug, prNumber, contextNodeList);
- expect(treeStatusFlag, true);
- });
-
- test('Validate tree status is set does not contain slug.', () {
- slug = github.RepositorySlug('flutter', 'infra');
- final List<ContextNode> contextNodeList = getContextNodeListFromJson(repositoryStatusesMock);
- expect(contextNodeList.isEmpty, false);
-
- /// The status must be uppercase as the original code is expecting this.
- convertContextNodeStatuses(contextNodeList);
- final bool treeStatusFlag = ciSuccessful.treeStatusCheck(slug, prNumber, contextNodeList);
- expect(treeStatusFlag, true);
- });
-
- test('Validate tree status is set but context does not match slug.', () {
- slug = github.RepositorySlug('flutter', 'flutter');
- final List<ContextNode> contextNodeList = getContextNodeListFromJson(repositoryStatusesNonLuciFlutterMock);
-
- /// The status must be uppercase as the original code is expecting this.
- convertContextNodeStatuses(contextNodeList);
- final bool treeStatusFlag = ciSuccessful.treeStatusCheck(slug, prNumber, contextNodeList);
- expect(treeStatusFlag, false);
- });
- });
-
- group('validate', () {
- test('Commit has a null status, no statuses to verify.', () {
- final Map<String, dynamic> queryResultJsonDecode =
- jsonDecode(nullStatusCommitRepositoryJson) as Map<String, dynamic>;
- final QueryResult queryResult = QueryResult.fromJson(queryResultJsonDecode);
- expect(queryResult, isNotNull);
- final PullRequest pr = queryResult.repository!.pullRequest!;
- expect(pr, isNotNull);
- final Commit commit = pr.commits!.nodes!.single.commit!;
- expect(commit, isNotNull);
- expect(commit.status, isNull);
-
- final github.PullRequest npr = generatePullRequest();
- githubService.checkRunsData = checkRunsMock;
-
- ciSuccessful.validate(queryResult, npr).then((value) {
- // fails because in this case there is only a single fail status
- expect(false, value.result);
- // Remove label.
- expect(value.action, Action.IGNORE_TEMPORARILY);
- expect(value.message, 'Hold to wait for the tree status ready.');
- });
- });
-
- test('Commit has no statuses to verify.', () {
- final Map<String, dynamic> queryResultJsonDecode = jsonDecode(noStatusInCommitJson) as Map<String, dynamic>;
- final QueryResult queryResult = QueryResult.fromJson(queryResultJsonDecode);
- expect(queryResult, isNotNull);
- final PullRequest pr = queryResult.repository!.pullRequest!;
- expect(pr, isNotNull);
-
- final github.PullRequest npr = generatePullRequest();
- githubService.checkRunsData = checkRunsMock;
-
- ciSuccessful.validate(queryResult, npr).then((value) {
- // fails because in this case there is only a single fail status
- expect(false, value.result);
- // Remove label.
- expect(value.action, Action.IGNORE_TEMPORARILY);
- expect(value.message, 'Hold to wait for the tree status ready.');
- });
- });
-
- // When branch is default we need to wait for the tree status if it is not
- // present.
- test('Commit has no statuses to verify.', () {
- final Map<String, dynamic> queryResultJsonDecode = jsonDecode(noStatusInCommitJson) as Map<String, dynamic>;
- final QueryResult queryResult = QueryResult.fromJson(queryResultJsonDecode);
- expect(queryResult, isNotNull);
- final PullRequest pr = queryResult.repository!.pullRequest!;
- expect(pr, isNotNull);
-
- final github.PullRequest npr = generatePullRequest();
- githubService.checkRunsData = checkRunsMock;
-
- ciSuccessful.validate(queryResult, npr).then((value) {
- // fails because in this case there is only a single fail status
- expect(false, value.result);
- // Remove label.
- expect(value.action, Action.IGNORE_TEMPORARILY);
- expect(value.message, 'Hold to wait for the tree status ready.');
- });
- });
-
- // Test for when the base branch is not default, we should not check the
- // tree status as it does not apply.
- test('Commit has no statuses to verify and base branch is not default.', () {
- final Map<String, dynamic> queryResultJsonDecode = jsonDecode(noStatusInCommitJson) as Map<String, dynamic>;
- final QueryResult queryResult = QueryResult.fromJson(queryResultJsonDecode);
- expect(queryResult, isNotNull);
- final PullRequest pr = queryResult.repository!.pullRequest!;
- expect(pr, isNotNull);
-
- final github.PullRequest npr = generatePullRequest(baseRef: 'test_feature');
- githubService.checkRunsData = checkRunsMock;
-
- ciSuccessful.validate(queryResult, npr).then((value) {
- expect(true, value.result);
- // Remove label.
- expect(value.action, Action.REMOVE_LABEL);
- expect(value.message, isEmpty);
- });
- });
-
- test('Commit has statuses to verify, action remove label, no message.', () {
- final Map<String, dynamic> queryResultJsonDecode =
- jsonDecode(nonNullStatusSUCCESSCommitRepositoryJson) as Map<String, dynamic>;
- final QueryResult queryResult = QueryResult.fromJson(queryResultJsonDecode);
- expect(queryResult, isNotNull);
- final PullRequest pr = queryResult.repository!.pullRequest!;
- expect(pr, isNotNull);
- final Commit commit = pr.commits!.nodes!.single.commit!;
- expect(commit, isNotNull);
- expect(commit.status, isNotNull);
-
- final github.PullRequest npr = generatePullRequest();
- githubService.checkRunsData = checkRunsMock;
-
- ciSuccessful.validate(queryResult, npr).then((value) {
- // No failure.
- expect(value.result, isTrue);
- // Remove label.
- expect((value.action == Action.REMOVE_LABEL), isTrue);
- expect(value.message, isEmpty);
- });
- });
-
- test('Commit has statuses to verify, action ignore failure, no message.', () {
- final Map<String, dynamic> queryResultJsonDecode =
- jsonDecode(nonNullStatusFAILURECommitRepositoryJson) as Map<String, dynamic>;
- final QueryResult queryResult = QueryResult.fromJson(queryResultJsonDecode);
- expect(queryResult, isNotNull);
- final PullRequest pr = queryResult.repository!.pullRequest!;
- expect(pr, isNotNull);
- final Commit commit = pr.commits!.nodes!.single.commit!;
- expect(commit, isNotNull);
- expect(commit.status, isNotNull);
-
- final github.PullRequest npr = generatePullRequest(labelName: 'warning: land on red to fix tree breakage');
- githubService.checkRunsData = checkRunsMock;
-
- ciSuccessful.validate(queryResult, npr).then((value) {
- // No failure.
- expect(value.result, isTrue);
- // Remove label.
- expect((value.action == Action.IGNORE_FAILURE), isTrue);
- expect(value.message, isEmpty);
- });
- });
-
- test('Commit has statuses to verify, action failure, no message.', () {
- final Map<String, dynamic> queryResultJsonDecode =
- jsonDecode(nonNullStatusFAILURECommitRepositoryJson) as Map<String, dynamic>;
- final QueryResult queryResult = QueryResult.fromJson(queryResultJsonDecode);
- expect(queryResult, isNotNull);
- final PullRequest pr = queryResult.repository!.pullRequest!;
- expect(pr, isNotNull);
- final Commit commit = pr.commits!.nodes!.single.commit!;
- expect(commit, isNotNull);
- expect(commit.status, isNotNull);
-
- final github.PullRequest npr = generatePullRequest();
- githubService.checkRunsData = checkRunsMock;
-
- ciSuccessful.validate(queryResult, npr).then((value) {
- // No failure.
- expect(false, value.result);
- // Remove label.
- expect((value.action == Action.IGNORE_TEMPORARILY), isTrue);
- expect(value.message, isEmpty);
- });
- });
- });
-
- group('Validate empty message is not returned.', () {
- setUp(() {
- githubService = FakeGithubService(client: MockGitHub());
- config = FakeConfig(githubService: githubService);
- ciSuccessful = CiSuccessful(config: config);
- slug = github.RepositorySlug('flutter', 'cocoon');
- config.repositoryConfigurationMock = RepositoryConfiguration.fromYaml(sampleConfigNoOverride);
- });
-
- test('returns correct message when validation fails', () async {
- final PullRequestHelper flutterRequest = PullRequestHelper(
- prNumber: 0,
- lastCommitHash: oid,
- reviews: <PullRequestReviewHelper>[],
- );
-
- githubService.checkRunsData = failedCheckRunsMock;
- final github.PullRequest pullRequest = generatePullRequest(prNumber: 0, repoName: slug.name);
- final QueryResult queryResult = createQueryResult(flutterRequest);
-
- final ValidationResult validationResult = await ciSuccessful.validate(queryResult, pullRequest);
-
- expect(validationResult.result, false);
- expect(
- validationResult.message,
- '- The status or check suite [failed_checkrun](https://example.com) has failed. Please fix the issues identified (or deflake) before re-applying this label.\n',
- );
- });
- });
-}
diff --git a/auto_submit/test/validations/ci_successful_test_data.dart b/auto_submit/test/validations/ci_successful_test_data.dart
deleted file mode 100644
index 9638790..0000000
--- a/auto_submit/test/validations/ci_successful_test_data.dart
+++ /dev/null
@@ -1,179 +0,0 @@
-// Copyright 2022 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-const String noStatusInCommitJson = '''
-{
- "repository": {
- "pullRequest": {
- "author": {
- "login": "author1"
- },
- "authorAssociation": "MEMBER",
- "id": "PR_kwDOA8VHis43rs4_",
- "title": "[dependabot] Remove human reviewers",
- "commits": {
- "nodes":[
- {
- "commit": {
- "abbreviatedOid": "4009ecc",
- "oid": "4009ecc0b6dbf5cb19cb97472147063e7368ec10",
- "committedDate": "2022-05-11T22:35:02Z",
- "pushedDate": "2022-05-11T22:35:03Z",
- "status": {
- "contexts":[
-
- ]
- }
- }
- }
- ]
- },
- "reviews": {
- "nodes": [
- {
- "author": {
- "login": "keyonghan"
- },
- "authorAssociation": "MEMBER",
- "state": "APPROVED"
- }
- ]
- }
- }
- }
- }
-''';
-
-const String nullStatusCommitRepositoryJson = '''
- {
- "repository": {
- "pullRequest": {
- "author": {
- "login": "author1"
- },
- "authorAssociation": "MEMBER",
- "id": "PR_kwDOA8VHis43rs4_",
- "title": "[dependabot] Remove human reviewers",
- "commits": {
- "nodes":[
- {
- "commit": {
- "abbreviatedOid": "4009ecc",
- "oid": "4009ecc0b6dbf5cb19cb97472147063e7368ec10",
- "committedDate": "2022-05-11T22:35:02Z",
- "pushedDate": "2022-05-11T22:35:03Z",
- "status": null
- }
- }
- ]
- },
- "reviews": {
- "nodes": [
- {
- "author": {
- "login": "keyonghan"
- },
- "authorAssociation": "MEMBER",
- "state": "APPROVED"
- }
- ]
- }
- }
- }
- }
- ''';
-
-const String nonNullStatusSUCCESSCommitRepositoryJson = '''
- {
- "repository": {
- "pullRequest": {
- "author": {
- "login": "author1"
- },
- "authorAssociation": "MEMBER",
- "id": "PR_kwDOA8VHis43rs4_",
- "title": "[dependabot] Remove human reviewers",
- "commits": {
- "nodes":[
- {
- "commit": {
- "abbreviatedOid": "4009ecc",
- "oid": "4009ecc0b6dbf5cb19cb97472147063e7368ec10",
- "committedDate": "2022-05-11T22:35:02Z",
- "pushedDate": "2022-05-11T22:35:03Z",
- "status": {
- "contexts":[
- {
- "context":"tree-status",
- "state":"SUCCESS",
- "targetUrl":"https://ci.example.com/1000/output"
- }
- ]
- }
- }
- }
- ]
- },
- "reviews": {
- "nodes": [
- {
- "author": {
- "login": "keyonghan"
- },
- "authorAssociation": "MEMBER",
- "state": "APPROVED"
- }
- ]
- }
- }
- }
- }
- ''';
-
-const String nonNullStatusFAILURECommitRepositoryJson = '''
- {
- "repository": {
- "pullRequest": {
- "author": {
- "login": "author1"
- },
- "authorAssociation": "MEMBER",
- "id": "PR_kwDOA8VHis43rs4_",
- "title": "[dependabot] Remove human reviewers",
- "commits": {
- "nodes":[
- {
- "commit": {
- "abbreviatedOid": "4009ecc",
- "oid": "4009ecc0b6dbf5cb19cb97472147063e7368ec10",
- "committedDate": "2022-05-11T22:35:02Z",
- "pushedDate": "2022-05-11T22:35:03Z",
- "status": {
- "contexts":[
- {
- "context":"tree-status",
- "state":"FAILURE",
- "targetUrl":"https://ci.example.com/1000/output"
- }
- ]
- }
- }
- }
- ]
- },
- "reviews": {
- "nodes": [
- {
- "author": {
- "login": "keyonghan"
- },
- "authorAssociation": "MEMBER",
- "state": "APPROVED"
- }
- ]
- }
- }
- }
- }
- ''';
diff --git a/auto_submit/test/validations/mergeable_test.dart b/auto_submit/test/validations/mergeable_test.dart
deleted file mode 100644
index a75c206..0000000
--- a/auto_submit/test/validations/mergeable_test.dart
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright 2023 The Flutter 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 'package:auto_submit/model/auto_submit_query_result.dart' as auto;
-import 'package:auto_submit/validations/mergeable.dart';
-import 'package:auto_submit/validations/validation.dart';
-import 'package:github/github.dart' as github;
-import 'package:test/test.dart';
-
-import '../requests/github_webhook_test_data.dart';
-import '../src/service/fake_config.dart';
-import '../src/service/fake_github_service.dart';
-import '../src/service/fake_graphql_client.dart';
-import '../utilities/mocks.dart';
-import '../utilities/utils.dart';
-
-void main() {
- late Mergeable mergeable;
- late FakeConfig config;
- late FakeGithubService githubService;
- late FakeGraphQLClient githubGraphQLClient;
-
- setUp(() {
- githubGraphQLClient = FakeGraphQLClient();
- githubService = FakeGithubService(client: MockGitHub());
- config = FakeConfig(githubService: githubService, githubGraphQLClient: githubGraphQLClient);
- mergeable = Mergeable(config: config);
- });
-
- test('Pull request is mergeable', () async {
- const String org = 'flutter';
- const String repo = 'flutter';
-
- final PullRequestHelper flutterRequest = PullRequestHelper(
- prNumber: 0,
- lastCommitHash: oid,
- reviews: <PullRequestReviewHelper>[],
- );
- final auto.QueryResult queryResult = createQueryResult(flutterRequest);
-
- final github.PullRequest pullRequest = generatePullRequest(
- mergeable: true,
- login: org,
- repoName: repo,
- );
-
- final ValidationResult processMergeResult = await mergeable.validate(
- queryResult,
- pullRequest,
- );
- expect(processMergeResult.result, isTrue);
- expect(processMergeResult.message, 'Pull request flutter/flutter/1347 is mergeable');
- });
-
- test('Pull request mergeability has not been determined', () async {
- const String org = 'flutter';
- const String repo = 'flutter';
-
- final PullRequestHelper flutterRequest = PullRequestHelper(
- prNumber: 0,
- lastCommitHash: oid,
- reviews: <PullRequestReviewHelper>[],
- mergeableState: auto.MergeableState.UNKNOWN,
- );
- final auto.QueryResult queryResult = createQueryResult(flutterRequest);
-
- final github.PullRequest pullRequest = generatePullRequest(
- mergeable: null,
- login: org,
- repoName: repo,
- );
- githubService.pullRequestData = pullRequest;
-
- final ValidationResult processMergeResult = await mergeable.validate(
- queryResult,
- pullRequest,
- );
- expect(processMergeResult.result, isFalse);
- expect(
- processMergeResult.message,
- 'Mergeability of pull request flutter/flutter/1347 could not be determined at time of merge.',
- );
- });
-
- test('Pull request cannot be merged', () async {
- const String org = 'flutter';
- const String repo = 'flutter';
-
- final github.PullRequest pullRequest = generatePullRequest(
- mergeable: false,
- login: org,
- repoName: repo,
- );
- githubService.pullRequestData = pullRequest;
-
- final PullRequestHelper flutterRequest = PullRequestHelper(
- prNumber: 0,
- lastCommitHash: oid,
- reviews: <PullRequestReviewHelper>[],
- mergeableState: auto.MergeableState.CONFLICTING,
- );
- final auto.QueryResult queryResult = createQueryResult(flutterRequest);
-
- final ValidationResult processMergeResult = await mergeable.validate(
- queryResult,
- pullRequest,
- );
- expect(processMergeResult.result, isFalse);
- expect(processMergeResult.message, 'Pull request flutter/flutter/1347 is not in a mergeable state.');
- });
-}
diff --git a/auto_submit/test/validations/revert_test_data.dart b/auto_submit/test/validations/revert_test_data.dart
deleted file mode 100644
index 24de131..0000000
--- a/auto_submit/test/validations/revert_test_data.dart
+++ /dev/null
@@ -1,767 +0,0 @@
-// Copyright 2022 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-const String queryResultRepositoryOwnerJson = '''
-{
- "repository": {
- "pullRequest": {
- "author": {
- "login": "author1"
- },
- "id": "PR_kwDOA8VHis43rs4_",
- "title": "Revert Trailing comma analysis",
- "commits": {
- "nodes":[
- {
- "commit": {
- "abbreviatedOid": "4009ecc",
- "oid": "4009ecc0b6dbf5cb19cb97472147063e7368ec10",
- "committedDate": "2022-05-11T22:35:02Z",
- "pushedDate": "2022-05-11T22:35:03Z",
- "status": {
- "contexts":[
-
- ]
- }
- }
- }
- ]
- },
- "reviews": {
-
- }
- }
- }
- }
-''';
-
-const String queryResultRepositoryContributorJson = '''
-{
- "repository": {
- "pullRequest": {
- "author": {
- "login": "author1"
- },
- "id": "PR_kwDOA8VHis43rs4_",
- "title": "Revert Trailing comma analysis",
- "commits": {
- "nodes":[
- {
- "commit": {
- "abbreviatedOid": "4009ecc",
- "oid": "4009ecc0b6dbf5cb19cb97472147063e7368ec10",
- "committedDate": "2022-05-11T22:35:02Z",
- "pushedDate": "2022-05-11T22:35:03Z",
- "status": {
- "contexts":[
-
- ]
- }
- }
- }
- ]
- },
- "reviews": {
-
- }
- }
- }
- }
-''';
-
-const String revertPullRequestJson = '''
- {
- "url": "https://api.github.com/repos/flutter/cocoon/pulls/2047",
- "id": 1023297178,
- "node_id": "PR_kwDOA8VHis48_kaa",
- "number": 2047,
- "state": "open",
- "locked": false,
- "title": "Revert Trailing comma analysis",
- "user": {
- "login": "ricardoamador",
- "id": 32242716
- },
- "body": "Reverts flutter/cocoon#2024",
- "created_at": "2022-08-10T23:27:49Z",
- "updated_at": "2022-08-12T16:57:35Z",
- "labels": [],
- "head": {
- "label": "flutter:revert-2024-trailing_comma_analysis",
- "ref": "revert-2024-trailing_comma_analysis",
- "sha": "8c8d14b5eda928e25b43fc76da94f78ab11d99d7",
- "user": {
- "login": "flutter",
- "id": 14101776,
- "node_id": "MDEyOk9yZ2FuaXphdGlvbjE0MTAxNzc2"
- },
- "repo": {
- "id": 63260554,
- "node_id": "MDEwOlJlcG9zaXRvcnk2MzI2MDU1NA==",
- "name": "cocoon",
- "full_name": "flutter/cocoon",
- "private": false,
- "owner": {
- "login": "flutter",
- "id": 14101776,
- "node_id": "MDEyOk9yZ2FuaXphdGlvbjE0MTAxNzc2",
- "avatar_url": "https://avatars.githubusercontent.com/u/14101776?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/flutter",
- "html_url": "https://github.com/flutter",
- "type": "Organization",
- "site_admin": false
- },
- "default_branch": "main"
- }
- },
- "base": {
- "label": "flutter:main",
- "ref": "main",
- "sha": "04a33c0719e114a8afffb2e31e29c38ac6c9f0a7",
- "user": {
- "login": "flutter",
- "id": 14101776,
- "node_id": "MDEyOk9yZ2FuaXphdGlvbjE0MTAxNzc2"
- },
- "repo": {
- "id": 63260554,
- "node_id": "MDEwOlJlcG9zaXRvcnk2MzI2MDU1NA==",
- "name": "cocoon",
- "full_name": "flutter/cocoon",
- "private": false,
- "owner": {
- "login": "flutter",
- "id": 14101776,
- "node_id": "MDEyOk9yZ2FuaXphdGlvbjE0MTAxNzc2",
- "avatar_url": "https://avatars.githubusercontent.com/u/14101776?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/flutter",
- "html_url": "https://github.com/flutter",
- "type": "Organization",
- "site_admin": false
- },
- "default_branch": "main"
- }
- },
- "author": "ricardoamador",
- "auto_merge": null,
- "active_lock_reason": null,
- "merged": false,
- "mergeable": true,
- "rebaseable": true,
- "mergeable_state": "blocked",
- "merged_by": null,
- "comments": 1,
- "review_comments": 0,
- "maintainer_can_modify": false,
- "commits": 1,
- "additions": 325,
- "deletions": 472,
- "changed_files": 25
-}
-''';
-
-const String revertPullRequestFilesJson = '''
- [
- {
- "filename": "dashboard/analysis_options.yaml",
- "additions": 0,
- "deletions": 1,
- "changes": 1
- },
- {
- "filename": "dashboard/lib/build_dashboard_page.dart",
- "additions": 35,
- "deletions": 45,
- "changes": 80
- },
- {
- "filename": "dashboard/lib/index_page.dart",
- "additions": 10,
- "deletions": 12,
- "changes": 22
- },
- {
- "filename": "dashboard/lib/logic/brooks.dart",
- "additions": 18,
- "deletions": 20,
- "changes": 38
- },
- {
- "filename": "dashboard/lib/logic/links.dart",
- "additions": 15,
- "deletions": 18,
- "changes": 33
- },
- {
- "filename": "dashboard/lib/logic/task_grid_filter.dart",
- "additions": 4,
- "deletions": 12,
- "changes": 16
- },
- {
- "filename": "dashboard/lib/main.dart",
- "additions": 6,
- "deletions": 7,
- "changes": 13
- },
- {
- "filename": "dashboard/lib/service/appengine_cocoon.dart",
- "additions": 16,
- "deletions": 23,
- "changes": 39
- },
- {
- "filename": "dashboard/lib/service/dev_cocoon.dart",
- "additions": 8,
- "deletions": 15,
- "changes": 23
- },
- {
- "filename": "dashboard/lib/widgets/lattice.dart",
- "additions": 18,
- "deletions": 22,
- "changes": 40
- },
- {
- "filename": "dashboard/lib/widgets/luci_task_attempt_summary.dart",
- "additions": 4,
- "deletions": 5,
- "changes": 9
- },
- {
- "filename": "dashboard/lib/widgets/sign_in_button.dart",
- "additions": 10,
- "deletions": 12,
- "changes": 22
- },
- {
- "filename": "dashboard/lib/widgets/task_grid.dart",
- "additions": 13,
- "deletions": 17,
- "changes": 30
- },
- {
- "filename": "dashboard/lib/widgets/task_overlay.dart",
- "additions": 2,
- "deletions": 3,
- "changes": 5
- },
- {
- "filename": "dashboard/pubspec.lock",
- "additions": 1,
- "deletions": 1,
- "changes": 2
- },
- {
- "filename": "dashboard/test/build_dashboard_page_test.dart",
- "additions": 21,
- "deletions": 33,
- "changes": 54
- },
- {
- "filename": "dashboard/test/index_page_test.dart",
- "additions": 4,
- "deletions": 7,
- "changes": 11
- },
- {
- "filename": "dashboard/test/logic/qualified_task_test.dart",
- "additions": 9,
- "deletions": 16,
- "changes": 25
- },
- {
- "filename": "dashboard/test/logic/task_grid_filter_test.dart",
- "additions": 11,
- "deletions": 18,
- "changes": 29
- },
- {
- "filename": "dashboard/test/service/appengine_cocoon_test.dart",
- "additions": 74,
- "deletions": 112,
- "changes": 186
- },
- {
- "filename": "dashboard/test/service/google_authentication_test.dart",
- "additions": 2,
- "deletions": 4,
- "changes": 6
- },
- {
- "filename": "dashboard/test/state/build_test.dart",
- "additions": 20,
- "deletions": 31,
- "changes": 51
- },
- {
- "filename": "dashboard/test/utils/fake_build.dart",
- "additions": 6,
- "deletions": 10,
- "changes": 16
- },
- {
- "filename": "dashboard/test/utils/golden.dart",
- "additions": 4,
- "deletions": 6,
- "changes": 10
- },
- {
- "filename": "dashboard/test/widgets/accessibility_test.dart",
- "additions": 14,
- "deletions": 22,
- "changes": 36
- }
- ]
-''';
-
-const String originalPullRequestJson = '''
- {
- "url": "https://api.github.com/repos/flutter/cocoon/pulls/2024",
- "id": 1012225049,
- "node_id": "PR_kwDOA8VHis48VVQZ",
- "number": 2024,
- "state": "closed",
- "locked": false,
- "title": "Trailing comma analysis",
- "user": {
- "login": "ricardoamador",
- "id": 32242716
- },
- "body": "## Description Add trailing comma requirement to analysis file. * if you had to change anything in the [flutter/tests] repo, include a link to the migration guide as per the [breaking change policy].* ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read the [Flutter Style Guide] _recently_, and have followed its advice. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/wiki/Tree-hygiene#overview [Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene [test-exempt]: https://github.com/flutter/flutter/wiki/Tree-hygiene#tests [Flutter Style Guide]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/wiki/Chat",
- "created_at": "2022-07-29T17:56:31Z",
- "labels": [
- {
- "id": 3960015931,
- "node_id": "LA_kwDOA8VHis7sCQw7",
- "url": "https://api.github.com/repos/flutter/cocoon/labels/autosubmit",
- "name": "autosubmit",
- "color": "0E8A16",
- "default": false,
- "description": "Merge PR when tree becomes green via auto submit App"
- }
- ],
- "head": {
- "label": "ricardoamador:trailing_comma_analysis",
- "ref": "trailing_comma_analysis",
- "sha": "8f2f3d7157345fc1d1c497e1b8106a1d9716fac8",
- "user": {
- "login": "ricardoamador",
- "id": 32242716,
- "node_id": "MDQ6VXNlcjMyMjQyNzE2"
- },
- "repo": {
- "id": 494596620,
- "node_id": "R_kgDOHXryDA",
- "name": "cocoon",
- "full_name": "ricardoamador/cocoon",
- "private": false,
- "owner": {
- "login": "ricardoamador",
- "id": 32242716,
- "node_id": "MDQ6VXNlcjMyMjQyNzE2",
- "avatar_url": "https://avatars.githubusercontent.com/u/32242716?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/ricardoamador",
- "html_url": "https://github.com/ricardoamador",
- "type": "User",
- "site_admin": false
- },
- "default_branch": "main"
- }
- },
- "base": {
- "label": "flutter:main",
- "ref": "main",
- "sha": "bb71fb95db4b1ac61194cd2a12134e9a469960a9",
- "user": {
- "login": "flutter",
- "id": 14101776,
- "node_id": "MDEyOk9yZ2FuaXphdGlvbjE0MTAxNzc2"
- },
- "repo": {
- "id": 63260554,
- "node_id": "MDEwOlJlcG9zaXRvcnk2MzI2MDU1NA==",
- "name": "cocoon",
- "full_name": "flutter/cocoon",
- "private": false,
- "owner": {
- "login": "flutter",
- "id": 14101776,
- "node_id": "MDEyOk9yZ2FuaXphdGlvbjE0MTAxNzc2",
- "avatar_url": "https://avatars.githubusercontent.com/u/14101776?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/flutter",
- "html_url": "https://github.com/flutter",
- "type": "Organization",
- "site_admin": false
- },
- "default_branch": "main"
- }
- },
- "auto_merge": null,
- "active_lock_reason": null,
- "merged": true,
- "mergeable": null,
- "rebaseable": null,
- "mergeable_state": "unknown",
- "merged_by": {
- "login": "auto-submit[bot]",
- "id": 98614782,
- "node_id": "BOT_kgDOBeC9_g",
- "avatar_url": "https://avatars.githubusercontent.com/u/14101776?v=4",
- "gravatar_id": "",
- "url": "https://api.github.com/users/auto-submit%5Bbot%5D",
- "html_url": "https://github.com/apps/auto-submit",
- "type": "Bot",
- "site_admin": false
- },
- "comments": 0,
- "review_comments": 0,
- "maintainer_can_modify": false,
- "commits": 4,
- "additions": 472,
- "deletions": 325,
- "changed_files": 25
-}
-''';
-
-const String originalPullRequestFilesJson = '''
- [
- {
- "filename": "dashboard/analysis_options.yaml",
- "additions": 1,
- "deletions": 0,
- "changes": 1
- },
- {
- "filename": "dashboard/lib/build_dashboard_page.dart",
- "additions": 45,
- "deletions": 35,
- "changes": 80
- },
- {
- "filename": "dashboard/lib/index_page.dart",
- "additions": 12,
- "deletions": 10,
- "changes": 22
- },
- {
- "filename": "dashboard/lib/logic/brooks.dart",
- "additions": 20,
- "deletions": 18,
- "changes": 38
- },
- {
- "filename": "dashboard/lib/logic/links.dart",
- "additions": 18,
- "deletions": 15,
- "changes": 33
- },
- {
- "filename": "dashboard/lib/logic/task_grid_filter.dart",
- "additions": 12,
- "deletions": 4,
- "changes": 16
- },
- {
- "filename": "dashboard/lib/main.dart",
- "additions": 7,
- "deletions": 6,
- "changes": 13
- },
- {
- "filename": "dashboard/lib/service/appengine_cocoon.dart",
- "additions": 23,
- "deletions": 16,
- "changes": 39
- },
- {
- "filename": "dashboard/lib/service/dev_cocoon.dart",
- "additions": 15,
- "deletions": 8,
- "changes": 23
- },
- {
- "filename": "dashboard/lib/widgets/lattice.dart",
- "additions": 22,
- "deletions": 18,
- "changes": 40
- },
- {
- "filename": "dashboard/lib/widgets/luci_task_attempt_summary.dart",
- "additions": 5,
- "deletions": 4,
- "changes": 9
- },
- {
- "filename": "dashboard/lib/widgets/sign_in_button.dart",
- "additions": 12,
- "deletions": 10,
- "changes": 22
- },
- {
- "filename": "dashboard/lib/widgets/task_grid.dart",
- "additions": 17,
- "deletions": 13,
- "changes": 30
- },
- {
- "filename": "dashboard/lib/widgets/task_overlay.dart",
- "additions": 3,
- "deletions": 2,
- "changes": 5
- },
- {
- "filename": "dashboard/pubspec.lock",
- "additions": 1,
- "deletions": 1,
- "changes": 2
- },
- {
- "filename": "dashboard/test/build_dashboard_page_test.dart",
- "additions": 33,
- "deletions": 21,
- "changes": 54
- },
- {
- "filename": "dashboard/test/index_page_test.dart",
- "additions": 7,
- "deletions": 4,
- "changes": 11
- },
- {
- "filename": "dashboard/test/logic/qualified_task_test.dart",
- "additions": 16,
- "deletions": 9,
- "changes": 25
- },
- {
- "filename": "dashboard/test/logic/task_grid_filter_test.dart",
- "additions": 18,
- "deletions": 11,
- "changes": 29
- },
- {
- "filename": "dashboard/test/service/appengine_cocoon_test.dart",
- "additions": 112,
- "deletions": 74,
- "changes": 186
- },
- {
- "filename": "dashboard/test/service/google_authentication_test.dart",
- "additions": 4,
- "deletions": 2,
- "changes": 6
- },
- {
- "filename": "dashboard/test/state/build_test.dart",
- "additions": 31,
- "deletions": 20,
- "changes": 51
- },
- {
- "filename": "dashboard/test/utils/fake_build.dart",
- "additions": 10,
- "deletions": 6,
- "changes": 16
- },
- {
- "filename": "dashboard/test/utils/golden.dart",
- "additions": 6,
- "deletions": 4,
- "changes": 10
- },
- {
- "filename": "dashboard/test/widgets/accessibility_test.dart",
- "additions": 22,
- "deletions": 14,
- "changes": 36
- }
- ]
-''';
-
-const String originalPullRequestFilesSubsetJson = '''
- [
- {
- "filename": "dashboard/analysis_options.yaml",
- "additions": 1,
- "deletions": 0,
- "changes": 1
- },
- {
- "filename": "dashboard/lib/build_dashboard_page.dart",
- "additions": 45,
- "deletions": 35,
- "changes": 80
- },
- {
- "filename": "dashboard/lib/index_page.dart",
- "additions": 12,
- "deletions": 10,
- "changes": 22
- },
- {
- "filename": "dashboard/lib/logic/brooks.dart",
- "additions": 20,
- "deletions": 18,
- "changes": 38
- },
- {
- "filename": "dashboard/lib/logic/links.dart",
- "additions": 18,
- "deletions": 15,
- "changes": 33
- },
- {
- "filename": "dashboard/lib/logic/task_grid_filter.dart",
- "additions": 12,
- "deletions": 4,
- "changes": 16
- },
- {
- "filename": "dashboard/lib/service/appengine_cocoon.dart",
- "additions": 23,
- "deletions": 16,
- "changes": 39
- },
- {
- "filename": "dashboard/lib/service/dev_cocoon.dart",
- "additions": 15,
- "deletions": 8,
- "changes": 23
- },
- {
- "filename": "dashboard/lib/widgets/lattice.dart",
- "additions": 22,
- "deletions": 18,
- "changes": 40
- },
- {
- "filename": "dashboard/lib/widgets/sign_in_button.dart",
- "additions": 12,
- "deletions": 10,
- "changes": 22
- },
- {
- "filename": "dashboard/lib/widgets/task_grid.dart",
- "additions": 17,
- "deletions": 13,
- "changes": 30
- },
- {
- "filename": "dashboard/lib/widgets/task_overlay.dart",
- "additions": 3,
- "deletions": 2,
- "changes": 5
- },
- {
- "filename": "dashboard/pubspec.lock",
- "additions": 1,
- "deletions": 1,
- "changes": 2
- },
- {
- "filename": "dashboard/test/build_dashboard_page_test.dart",
- "additions": 33,
- "deletions": 21,
- "changes": 54
- },
- {
- "filename": "dashboard/test/index_page_test.dart",
- "additions": 7,
- "deletions": 4,
- "changes": 11
- },
- {
- "filename": "dashboard/test/logic/qualified_task_test.dart",
- "additions": 16,
- "deletions": 9,
- "changes": 25
- },
- {
- "filename": "dashboard/test/logic/task_grid_filter_test.dart",
- "additions": 18,
- "deletions": 11,
- "changes": 29
- },
- {
- "filename": "dashboard/test/service/appengine_cocoon_test.dart",
- "additions": 112,
- "deletions": 74,
- "changes": 186
- },
- {
- "filename": "dashboard/test/service/google_authentication_test.dart",
- "additions": 4,
- "deletions": 2,
- "changes": 6
- },
- {
- "filename": "dashboard/test/state/build_test.dart",
- "additions": 31,
- "deletions": 20,
- "changes": 51
- },
- {
- "filename": "dashboard/test/utils/fake_build.dart",
- "additions": 10,
- "deletions": 6,
- "changes": 16
- },
- {
- "filename": "dashboard/test/utils/golden.dart",
- "additions": 6,
- "deletions": 4,
- "changes": 10
- },
- {
- "filename": "dashboard/test/widgets/accessibility_test.dart",
- "additions": 22,
- "deletions": 14,
- "changes": 36
- }
- ]
-''';
-
-const String ciyamlCheckRun = '''
-{
- "total_count": 1,
- "check_runs": [
- {
- "id": 8752872923,
- "name": "ci.yaml validation",
- "head_sha": "60612a38d705d00a234e0aabba08247fc0dda1ac",
- "status": "completed",
- "conclusion": "success",
- "started_at": "2022-10-06T20:50:57Z",
- "completed_at": "2022-10-06T20:51:40Z",
- "check_suite": {
- "id": 8654966141
- }
- }
- ]
-}
-''';
-
-const String ciyamlCheckRunNotComplete = '''
-{
- "total_count": 1,
- "check_runs": [
- {
- "id": 8752872923,
- "name": "ci.yaml validation",
- "head_sha": "60612a38d705d00a234e0aabba08247fc0dda1ac",
- "status": "in_progress",
- "started_at": "2022-10-06T20:50:57Z",
- "completed_at": "2022-10-06T20:51:40Z",
- "check_suite": {
- "id": 8654966141
- }
- }
- ]
-}
-''';
diff --git a/cipd_packages/README.md b/cipd_packages/README.md
deleted file mode 100644
index 9596db0..0000000
--- a/cipd_packages/README.md
+++ /dev/null
@@ -1,77 +0,0 @@
-# Flutter CIPD Packages
-
-This folder contains auto built/deployed public [CIPD packages](https://chrome-infra-packages.appspot.com/p/flutter)
-used by Flutter CI. These packages will be consumed from tests and auto cached to boost future runs.
-
-Please follow these steps to add a new package.
-
-## Creating the package build file
-
-Create a new sub directory under this folder named with the new package. Add a `build.sh` script for Linux/Mac or
-`build.bat` for Windows, and put it under a child dir `tool`. This is the main script to build this package. Add
-necessary lib files if needed.
-
-Note:
-
-1) we do not archive the binaries in the repo, and will auto build package artifacts and upload to CIPD via the bot.
-2) please add a code owner entry under section `cipd packages` in [CODEOWNERS](https://github.com/flutter/cocoon/blob/main/CODEOWNERS) file.
-
-## Add an entry to cocoon .ci.yaml
-
-This is to enable the auto build and upload. Please follow the following example format:
-```yaml
- - name: Mac <package_name>
- recipe: cocoon/cipd
- bringup: true
- properties:
- script: cipd_packages/<package_name>/tool/build.sh
- cipd_name: flutter/<package_name>/mac-amd64 # Use mac-arm64 for arm64 version.
- device_type: none
- runIf:
- - cipd_packages/<package_name>/**
- - .ci.yaml
-```
-
-Start with `bringup: true`, same as any other regular new target. Once validated in CI, remove it to enable running
-in the `prod` environment so that artifacts are to be uploaded to CIPD.
-
-Note: with `bringup: true`, the target will be executed in a staging environment and it validates only the logic
-and will not upload to CIPD.
-
-## Adding a reference to the CIPD package
-
-Until this step, artifacts are being uploaded to CIPD whenever a new commit is merged. It is useful to add a reference
-to the package, so that we can use the reference in the CI recipe. This way we won’t need to change the recipe
-whenever we update the package.
-
-Googlers have default access to add a reference to a package via:
-```sh
-cipd set-ref flutter/PackageName/mac-amd64 -ref Reference -version InstanceID
-```
-
-* Reference: e.g. major release versions. If not specified, `latest` will be used based on the latest package instance.
-* InstaneID: this can be obtained from the package page, e.g. [ruby](https://chrome-infra-packages.appspot.com/p/flutter/ruby/mac-amd64/+/TyvPskvefNRkTDmiDcwRHrdL_a2FQE_4wBojOqhxdtYC).
-
-Note: for non-Googler contributors, please file an [infra bug](https://github.com/flutter/flutter/issues/new?assignees=&labels=team-infra&projects=&template=6_infrastructure.yml) to make a reference request.
-
-## Supporting packages download from CI recipe
-
-Example CL: [51547 ](https://flutter-review.googlesource.com/c/recipes/+/51547)
-
-Refer to [CONTRIBUTING.md](https://flutter.googlesource.com/recipes/+/refs/heads/main/CONTRIBUTING.md) on how to
-contribute to recipes repository.
-
-## Adding/updating package dependency from .ci.yaml from different repositories
-
-This is the last step to enable the package usage in the real CI. Add or update the new package to either the platform level or
-target level entries for your targeted repository:
-``` yaml
-dependencies: >-
- [
- {"dependency": "chrome_and_driver", "version": "Reference"},
- ]
-```
-
-Note: use the `Reference` created above.
-
-More details about configuration setup can be found in [CI_YAML.md](https://github.com/flutter/cocoon/blob/main/CI_YAML.md).
\ No newline at end of file
diff --git a/cipd_packages/codesign/.gitignore b/cipd_packages/codesign/.gitignore
deleted file mode 100644
index 3c8a157..0000000
--- a/cipd_packages/codesign/.gitignore
+++ /dev/null
@@ -1,6 +0,0 @@
-# Files and directories created by pub.
-.dart_tool/
-.packages
-
-# Conventional directory for build output.
-build/
diff --git a/cipd_packages/codesign/LICENSE b/cipd_packages/codesign/LICENSE
deleted file mode 100644
index d5384ca..0000000
--- a/cipd_packages/codesign/LICENSE
+++ /dev/null
@@ -1,27 +0,0 @@
-Copyright 2016 The Flutter Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/cipd_packages/codesign/README.md b/cipd_packages/codesign/README.md
deleted file mode 100644
index ce23050..0000000
--- a/cipd_packages/codesign/README.md
+++ /dev/null
@@ -1,56 +0,0 @@
-# codesign
-
-A standalone tool to codesign Mac engine binaries.
-
-## Building
-
-This tool is meant to be published as an
-[AOT compiled binary](https://chrome-infra-packages.appspot.com/p/flutter/codesign)
-distributed via CIPD.
-
-Build the tool for different host platforms on corresponding machines. It will
-automatically download a suitable version of Dart to build the binary.
-
-To create the CIPD package, make sure that the `build/` folder does not exist.
-
-### Auto build
-
-Every new commit will trigger pre-submit builders to auto build a new version
-for different platforms without any tag/ref.
-
-When a new commit is submitted, post-submit builders will trigger the build of
-a new version of the cipd package, and tag the package with `latest`.
-
-### Manual build
-
-Running `tool/build.sh` will build an executable binary in
-the `build` folder. Then push to cipd by running
-
-```bash
-cipd create -in build \
- -name flutter/codesign/<os>-amd64 \
- -ref <ref> \
- -tag sha_timestamp:<revision>_<timestamp>
-```
-
-* os: `linux`, `mac`, or `windows`.
-* ref: `release` or `staging`
-
-## How to use
-
-`codesign` is the executable binary in the `build` folder, and can be called via
-
- ```bash
- ./codesign --[no-]dryrun
- --codesign-cert-name="FLUTTER.IO LLC"
- --codesign-team-id-file-path=/a/b/c.txt
- --codesign-appstore-id-file-path=/a/b/b.txt
- --app-specific-password-file-path=/a/b/a.txt
- --input-zip-file-path=/a/input.zip
- --output-zip-file-path=/b/output.zip
- ```
-
-Use `codesign --help` to learn more.
-
-Alternatively, if user has dart installed and does not wish to build a binary,
-codesign app can be invoked via `dart run bin/codesign.dart --<extra_flags>`.
diff --git a/cipd_packages/codesign/analysis_options.yaml b/cipd_packages/codesign/analysis_options.yaml
deleted file mode 100644
index f04c6cf..0000000
--- a/cipd_packages/codesign/analysis_options.yaml
+++ /dev/null
@@ -1 +0,0 @@
-include: ../../analysis_options.yaml
diff --git a/cipd_packages/codesign/bin/codesign.dart b/cipd_packages/codesign/bin/codesign.dart
deleted file mode 100644
index b5a73e9..0000000
--- a/cipd_packages/codesign/bin/codesign.dart
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright 2019 The Flutter 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:args/args.dart';
-import 'package:codesign/codesign.dart';
-import 'package:file/file.dart';
-import 'package:file/local.dart';
-import 'package:logging/logging.dart';
-import 'package:platform/platform.dart';
-import 'package:process/process.dart';
-
-/// Definitions of variables are included in help texts below.
-const String kHelpFlag = 'help';
-const String kDryrunFlag = 'dryrun';
-const String kCodesignCertNameOption = 'codesign-cert-name';
-const String kInputZipPathOption = 'input-zip-file-path';
-const String kOutputZipPathOption = 'output-zip-file-path';
-const String kAppSpecificPasswordOption = 'app-specific-password-file-path';
-const String kCodesignAppstoreIDOption = 'codesign-appstore-id-file-path';
-const String kCodesignTeamIDOption = 'codesign-team-id-file-path';
-
-/// Perform Mac code signing based on file paths.
-///
-/// By default, if a user does not specify a dryrun flag, or selects dryrun
-/// mode by providing the `--dryrun` flag, then [kDryrunFlag] is set to true,
-/// a quick sanity check is performed and the notarization process is skipped.
-/// On the other hand, if a user provides the flag `--no-dryrun`, [kDryrunFlag]
-/// will be set to false, and code signed artifacts will go through the notarization
-/// process.
-///
-/// For [kInputZipPathOption] and [kOutputZipPathOption], they are required parameter to specify the
-/// input and output locations.
-/// The codesign app will take the zip file located at the input location [kInputZipPathOption], and
-/// put codesigned zip at [kOutputZipPathOption]. The work of downloading and uploading the zip
-/// artifacts is delegated to recipe.
-/// For example, supply
-/// '--input-zip-file-path=/tmp/input.zip',
-/// and code sign app will code sign the artifacts located at /tmp/input.zip.
-///
-/// For [kAppSpecificPasswordOption], [kCodesignAppstoreIDOption] and [kCodesignTeamIDOption],
-/// they are file paths of the password files in the file system.
-/// Each of the file paths stores a single line of sensitive password.
-/// sensitive passwords include <CODESIGN_APPSTORE_ID>, <CODESIGN_TEAM_ID>, and <APP_SPECIFIC_PASSWORD>.
-/// For example, if a user supplies --app-specific-password-file-path=/tmp/passwords.txt,
-/// then we would be expecting a password file located at /tmp/passwords.txt.
-/// The password file should contain the password name APP-SPECIFIC-PASSWORD and its value, deliminated by a single colon.
-/// The content of a password file would look similar to:
-/// APP-SPECIFIC-PASSWORD:789
-///
-/// [kCodesignCertNameOption] is public information. For codesigning flutter artifacts,
-/// a user can provide values for this variable as shown in the example below.
-///
-/// Note: this tool uses Apple developer identity from keychain named build.keychain.
-/// You can create/delete build.keychain by following the steps below:
-/// /usr/bin/security create-keychain -p '' build.keychain # for create
-/// /usr/bin/security delete-keychain build.keychain # for delete
-///
-/// Usage:
-/// ```shell
-/// dart run bin/codesign.dart --[no-]dryrun
-/// --codesign-cert-name="FLUTTER.IO LLC"
-/// --codesign-team-id-file-path=/a/b/c.txt
-/// --codesign-appstore-id-file-path=/a/b/b.txt
-/// --app-specific-password-file-path=/a/b/a.txt
-/// --input-zip-file-path=/a/input.zip
-/// --output-zip-file-path=/b/output.zip
-/// ```
-Future<void> main(List<String> args) async {
- Logger.root.onRecord.listen((LogRecord record) {
- stdout.writeln(record.toString());
- });
-
- final ArgParser parser = ArgParser();
- parser
- ..addFlag(
- kHelpFlag,
- help: 'Prints usage info.',
- callback: (bool value) {
- if (value) {
- stdout.write('${parser.usage}\n');
- exit(1);
- }
- },
- )
- ..addOption(
- kCodesignCertNameOption,
- help: 'The name of the codesign certificate to be used when codesigning.'
- 'the name of the certificate for flutter, for example, is: FLUTTER.IO LLC',
- )
- ..addOption(
- kInputZipPathOption,
- help: 'File path to the unsigned artifact zip file.',
- )
- ..addOption(
- kOutputZipPathOption,
- help: 'File path to codesigned artifact zip file for output.',
- )
- ..addOption(
- kAppSpecificPasswordOption,
- help:
- 'The file path of a password file in file system. The password file stores the sensitive password <APP-SPECIFIC-PASSWORD>.',
- )
- ..addOption(
- kCodesignAppstoreIDOption,
- help:
- 'The file path of a password file in file system. The password file stores the sensitive password <CODESIGN_APPSTORE_ID>.',
- )
- ..addOption(
- kCodesignTeamIDOption,
- help:
- 'The file path of a password file in file system. The password file stores the sensitive password <CODESIGN_TEAM_ID>.',
- )
- ..addFlag(
- kDryrunFlag,
- defaultsTo: true,
- help: 'whether we are going to skip the notarization process.',
- );
-
- final ArgResults argResults = parser.parse(args);
-
- const Platform platform = LocalPlatform();
-
- final String codesignCertName = getValueFromArgs(kCodesignCertNameOption, argResults)!;
- final String inputZipPath = getValueFromArgs(kInputZipPathOption, argResults)!;
- final String outputZipPath = getValueFromArgs(kOutputZipPathOption, argResults)!;
- final String appSpecificPasswordFilePath = getValueFromArgs(kAppSpecificPasswordOption, argResults)!;
- final String codesignAppstoreIDFilePath = getValueFromArgs(kCodesignAppstoreIDOption, argResults)!;
- final String codesignTeamIDFilePath = getValueFromArgs(kCodesignTeamIDOption, argResults)!;
-
- final bool dryrun = argResults[kDryrunFlag] as bool;
-
- if (!platform.isMacOS) {
- throw CodesignException(
- 'Error! Expected operating system "macos", actual operating system is: '
- '"${platform.operatingSystem}"',
- );
- }
-
- const FileSystem fileSystem = LocalFileSystem();
- final Directory rootDirectory = fileSystem.systemTempDirectory.createTempSync('conductor_codesign');
- const ProcessManager processManager = LocalProcessManager();
-
- return FileCodesignVisitor(
- codesignCertName: codesignCertName,
- fileSystem: fileSystem,
- rootDirectory: rootDirectory,
- appSpecificPasswordFilePath: appSpecificPasswordFilePath,
- codesignAppstoreIDFilePath: codesignAppstoreIDFilePath,
- codesignTeamIDFilePath: codesignTeamIDFilePath,
- processManager: processManager,
- dryrun: dryrun,
- inputZipPath: inputZipPath,
- outputZipPath: outputZipPath,
- ).validateAll();
-}
diff --git a/cipd_packages/codesign/bin/verify.dart b/cipd_packages/codesign/bin/verify.dart
deleted file mode 100644
index f467d7a..0000000
--- a/cipd_packages/codesign/bin/verify.dart
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2019 The Flutter 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' as io;
-
-import 'package:codesign/verify.dart';
-import 'package:logging/logging.dart';
-
-Future<void> main(List<String> args) async {
- final Logger logger = Logger('root');
- logger.onRecord.listen((LogRecord record) {
- io.stdout.writeln(record.message);
- });
- if (args.length != 1) {
- logger.info('Usage: dart verify.dart [FILE]');
- io.exit(1);
- }
- if (!io.Platform.isMacOS) {
- logger.severe('This tool must be run from macOS.');
- io.exit(1);
- }
- final inputFile = args[0];
- final VerificationResult result = await VerificationService(
- binaryPath: inputFile,
- logger: logger,
- ).run();
- io.exit(result == VerificationResult.codesignedAndNotarized ? 0 : 1);
-}
diff --git a/cipd_packages/codesign/lib/codesign.dart b/cipd_packages/codesign/lib/codesign.dart
deleted file mode 100644
index 54fe0c8..0000000
--- a/cipd_packages/codesign/lib/codesign.dart
+++ /dev/null
@@ -1,6 +0,0 @@
-// Copyright 2019 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-export 'src/file_codesign_visitor.dart';
-export 'src/utils.dart';
diff --git a/cipd_packages/codesign/lib/src/file_codesign_visitor.dart b/cipd_packages/codesign/lib/src/file_codesign_visitor.dart
deleted file mode 100644
index 6d5da9c..0000000
--- a/cipd_packages/codesign/lib/src/file_codesign_visitor.dart
+++ /dev/null
@@ -1,499 +0,0 @@
-// Copyright 2019 The Flutter 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:async';
-import 'dart:io' as io;
-
-import 'package:file/file.dart';
-import 'package:process/process.dart';
-
-import 'log.dart';
-import 'utils.dart';
-
-/// Statuses reported by Apple's Notary Server.
-///
-/// See more:
-/// * https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution/customizing_the_notarization_workflow
-enum NotaryStatus {
- pending,
- failed,
- succeeded,
-}
-
-/// Codesign and notarize all files within a [RemoteArchive].
-class FileCodesignVisitor {
- FileCodesignVisitor({
- required this.codesignCertName,
- required this.fileSystem,
- required this.rootDirectory,
- required this.processManager,
- required this.inputZipPath,
- required this.outputZipPath,
- required this.appSpecificPasswordFilePath,
- required this.codesignAppstoreIDFilePath,
- required this.codesignTeamIDFilePath,
- this.dryrun = true,
- this.notarizationTimerDuration = const Duration(seconds: 5),
- }) {
- entitlementsFile = rootDirectory.childFile('Entitlements.plist')..writeAsStringSync(_entitlementsFileContents);
- }
-
- /// Temp [Directory] to download/extract files to.
- ///
- /// This file will be deleted if [validateAll] completes successfully.
- final Directory rootDirectory;
- final FileSystem fileSystem;
- final ProcessManager processManager;
-
- final String codesignCertName;
- final String inputZipPath;
- final String outputZipPath;
- final String appSpecificPasswordFilePath;
- final String codesignAppstoreIDFilePath;
- final String codesignTeamIDFilePath;
- final bool dryrun;
- final Duration notarizationTimerDuration;
-
- // 'Apple developer account email used for authentication with notary service.'
- late String codesignAppstoreId;
- // Unique password of the apple developer account.'
- late String appSpecificPassword;
- // Team-id is used by notary service for xcode version 13+.
- late String codesignTeamId;
-
- Set<String> fileWithEntitlements = <String>{};
- Set<String> fileWithoutEntitlements = <String>{};
- Set<String> fileConsumed = <String>{};
- Set<String> directoriesVisited = <String>{};
- Map<String, String> availablePasswords = {
- 'CODESIGN_APPSTORE_ID': '',
- 'CODESIGN_TEAM_ID': '',
- 'APP_SPECIFIC_PASSWORD': '',
- };
-
- late final File entitlementsFile;
-
- int _remoteDownloadIndex = 0;
- int get remoteDownloadIndex => _remoteDownloadIndex++;
-
- static const String _entitlementsFileContents = '''
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
- <dict>
- <key>com.apple.security.cs.allow-jit</key>
- <true/>
- <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
- <true/>
- <key>com.apple.security.cs.allow-dyld-environment-variables</key>
- <true/>
- <key>com.apple.security.network.client</key>
- <true/>
- <key>com.apple.security.network.server</key>
- <true/>
- <key>com.apple.security.cs.disable-library-validation</key>
- <true/>
- </dict>
-</plist>
-''';
- static final RegExp _notarytoolStatusCheckPattern = RegExp(r'[ ]*status: ([a-zA-z ]+)');
- static final RegExp _notarytoolRequestPattern = RegExp(r'id: ([a-z0-9-]+)');
-
- static const String fixItInstructions = '''
-Codesign test failed.
-
-We compared binary files in engine artifacts with those listed in
-entitlement.txt and withoutEntitlements.txt, and the binary files do not match.
-*entitlements.txt is the configuration file encoded in engine artifact zip,
-built by BUILD.gn and Ninja, to detail the list of entitlement files.
-Either an expected file was not found in *entitlements.txt, or an unexpected
-file was found in entitlements.txt.
-
-This usually happens during an engine roll.
-If this is a valid change, then BUILD.gn needs to be changed.
-Binaries that will run on a macOS host require entitlements, and
-binaries that run on an iOS device must NOT have entitlements.
-For example, if this is a new binary that runs on macOS host, add it
-to [entitlements.txt] file inside the zip artifact produced by BUILD.gn.
-If this is a new binary that needs to be run on iOS device, add it
-to [withoutEntitlements.txt].
-If there are obsolete binaries in entitlements configuration files, please delete or
-update these file paths accordingly.
-''';
-
- /// Read a single line of password stored at [passwordFilePath].
- Future<String> readPassword(String passwordFilePath) async {
- if (!(await fileSystem.file(passwordFilePath).exists())) {
- throw CodesignException('$passwordFilePath not found \n'
- 'make sure you have provided codesign credentials in a file \n');
- }
- return fileSystem.file(passwordFilePath).readAsString();
- }
-
- /// The entrance point of examining and code signing an engine artifact.
- Future<void> validateAll() async {
- codesignAppstoreId = await readPassword(codesignAppstoreIDFilePath);
- codesignTeamId = await readPassword(codesignTeamIDFilePath);
- appSpecificPassword = await readPassword(appSpecificPasswordFilePath);
-
- await processRemoteZip();
-
- log.info('Codesign completed. Codesigned zip is located at $outputZipPath.'
- 'If you have uploaded the artifacts back to google cloud storage, please delete'
- ' the folder $outputZipPath and $inputZipPath.');
- if (dryrun) {
- log.info('code signing dry run has completed, this is a quick sanity check without'
- 'going through the notary service. To run the full codesign process, use --no-dryrun flag.');
- }
- }
-
- /// Process engine artifacts from [inputZipPath] and kick start a
- /// recursive visit of its contents.
- ///
- /// Invokes [visitDirectory] to recursively visit the contents of the remote
- /// zip. Notarizes the engine artifact if [dryrun] is false.
- /// Returns null as result if [dryrun] is true.
- Future<String?> processRemoteZip() async {
- // download the zip file
- final File originalFile = rootDirectory.fileSystem.file(inputZipPath);
-
- // This is the starting directory of the unzipped artifact.
- final Directory parentDirectory = rootDirectory.childDirectory('single_artifact');
-
- await unzip(
- inputZip: originalFile,
- outDir: parentDirectory,
- processManager: processManager,
- );
-
- //extract entitlements file.
- fileWithEntitlements = await parseEntitlements(parentDirectory, true);
- fileWithoutEntitlements = await parseEntitlements(parentDirectory, false);
- log.info('parsed binaries with entitlements are $fileWithEntitlements');
- log.info('parsed binaries without entitlements are $fileWithEntitlements');
-
- // recursively visit extracted files
- await visitDirectory(directory: parentDirectory, parentVirtualPath: '');
-
- await zip(
- inputDir: parentDirectory,
- outputZipPath: outputZipPath,
- processManager: processManager,
- );
-
- await parentDirectory.delete(recursive: true);
-
- // `dryrun` flag defaults to true to save time for a faster sanity check
- if (!dryrun) {
- await notarize(fileSystem.file(outputZipPath));
-
- return outputZipPath;
- }
- return null;
- }
-
- /// Visit a [Directory] type while examining the file system extracted from an artifact.
- Future<void> visitDirectory({
- required Directory directory,
- required String parentVirtualPath,
- }) async {
- log.info('Visiting directory ${directory.absolute.path}');
- if (directoriesVisited.contains(directory.absolute.path)) {
- log.warning(
- 'Warning! You are visiting a directory that has been visited before, the directory is ${directory.absolute.path}',
- );
- }
- directoriesVisited.add(directory.absolute.path);
-
- await cleanupEntitlements(directory);
-
- final List<FileSystemEntity> entities = await directory.list(followLinks: false).toList();
- for (FileSystemEntity entity in entities) {
- if (entity is io.Link) {
- log.info('current file or direcotry ${entity.path} is a symlink to ${(entity as io.Link).targetSync()}, '
- 'codesign is therefore skipped for the current file or directory.');
- continue;
- }
- if (entity is io.Directory) {
- await visitDirectory(
- directory: directory.childDirectory(entity.basename),
- parentVirtualPath: joinEntitlementPaths(parentVirtualPath, entity.basename),
- );
- continue;
- }
- if (entity.basename == 'entitlements.txt' || entity.basename == 'without_entitlements.txt') {
- continue;
- }
- final FileType childType = getFileType(
- entity.absolute.path,
- processManager,
- );
- if (childType == FileType.zip) {
- await visitEmbeddedZip(
- zipEntity: entity,
- parentVirtualPath: parentVirtualPath,
- );
- } else if (childType == FileType.binary) {
- await visitBinaryFile(binaryFile: entity as File, parentVirtualPath: parentVirtualPath);
- }
- log.info('Child file of directory ${directory.basename} is ${entity.basename}');
- }
- }
-
- /// Unzip an [EmbeddedZip] and visit its children.
- Future<void> visitEmbeddedZip({
- required FileSystemEntity zipEntity,
- required String parentVirtualPath,
- }) async {
- log.info('This embedded file is ${zipEntity.path} and parentVirtualPath is $parentVirtualPath');
- final String currentFileName = zipEntity.basename;
- final Directory newDir = rootDirectory.childDirectory('embedded_zip_${zipEntity.absolute.path.hashCode}');
- await unzip(
- inputZip: zipEntity,
- outDir: newDir,
- processManager: processManager,
- );
-
- // the virtual file path is advanced by the name of the embedded zip
- final String currentZipEntitlementPath = joinEntitlementPaths(parentVirtualPath, currentFileName);
- await visitDirectory(
- directory: newDir,
- parentVirtualPath: currentZipEntitlementPath,
- );
- await zipEntity.delete();
- await zip(
- inputDir: newDir,
- outputZipPath: zipEntity.absolute.path,
- processManager: processManager,
- );
- await newDir.delete(recursive: true);
- }
-
- /// Visit and codesign a binary with / without entitlement.
- ///
- /// At this stage, the virtual [entitlementCurrentPath] accumulated through the recursive visit, is compared
- /// against the paths extracted from [fileWithEntitlements], to help determine if this file should be signed
- /// with entitlements.
- Future<void> visitBinaryFile({
- required File binaryFile,
- required String parentVirtualPath,
- int retryCount = 3,
- int sleepTime = 1,
- }) async {
- final String currentFileName = binaryFile.basename;
- final String entitlementCurrentPath = joinEntitlementPaths(parentVirtualPath, currentFileName);
-
- if (!fileWithEntitlements.contains(entitlementCurrentPath) &&
- !fileWithoutEntitlements.contains(entitlementCurrentPath)) {
- log.severe('the binary file $currentFileName is causing an issue. \n'
- 'This file is located at $entitlementCurrentPath in the flutter engine artifact.');
- log.severe('The system has detected a binary file at $entitlementCurrentPath. '
- 'But it is not in the entitlements configuration files you provided. '
- 'If this is a new engine artifact, please add it to one of the entitlements.txt files.');
- throw CodesignException(fixItInstructions);
- }
- log.info('signing file at path ${binaryFile.absolute.path}');
- log.info('the virtual entitlement path associated with file is $entitlementCurrentPath');
- log.info('the decision to sign with entitlement is ${fileWithEntitlements.contains(entitlementCurrentPath)}');
- fileConsumed.add(entitlementCurrentPath);
- if (dryrun) {
- return;
- }
- final List<String> args = <String>[
- '/usr/bin/codesign',
- '--keychain',
- 'build.keychain', // specify the keychain to look for cert
- '-f', // force
- '-s', // use the cert provided by next argument
- codesignCertName,
- binaryFile.absolute.path,
- '--timestamp', // add a secure timestamp
- '--options=runtime', // hardened runtime
- if (fileWithEntitlements.contains(entitlementCurrentPath)) ...<String>[
- '--entitlements',
- entitlementsFile.absolute.path,
- ],
- ];
-
- io.ProcessResult? result;
- while (retryCount > 0) {
- log.info('Executing: ${args.join(' ')}\n');
- result = await processManager.run(args);
- if (result.exitCode == 0) {
- return;
- }
-
- log.severe(
- 'Failed to codesign ${binaryFile.absolute.path} with args: ${args.join(' ')}\n'
- 'stdout:\n${(result.stdout as String).trim()}'
- 'stderr:\n${(result.stderr as String).trim()}',
- );
-
- retryCount -= 1;
- await Future.delayed(Duration(seconds: sleepTime));
- sleepTime *= 2;
- }
- throw CodesignException('Failed to codesign ${binaryFile.absolute.path} with args: ${args.join(' ')}\n'
- 'stdout:\n${(result!.stdout as String).trim()}\n'
- 'stderr:\n${(result.stderr as String).trim()}');
- }
-
- /// Delete codesign metadata at ALL places inside engine binary.
- ///
- /// Context: https://github.com/flutter/flutter/issues/126705. This is a temporary workaround.
- /// Once flutter tools is ready we can remove this logic.
- Future<void> cleanupEntitlements(Directory parent) async {
- final String metadataEntitlements = fileSystem.path.join(parent.path, 'entitlements.txt');
- final String metadataWithoutEntitlements = fileSystem.path.join(parent.path, 'without_entitlements.txt');
- for (String metadataPath in [metadataEntitlements, metadataWithoutEntitlements]) {
- if (await fileSystem.file(metadataPath).exists()) {
- log.warning('cleaning up codesign metadata at $metadataPath.');
- await fileSystem.file(metadataPath).delete();
- }
- }
- }
-
- /// Extract entitlements configurations from downloaded zip files.
- ///
- /// Parse and store codesign configurations detailed in configuration files.
- /// File paths of entilement files and non entitlement files will be parsed and stored in [fileWithEntitlements].
- Future<Set<String>> parseEntitlements(Directory parent, bool entitlements) async {
- final String entitlementFilePath = entitlements
- ? fileSystem.path.join(parent.path, 'entitlements.txt')
- : fileSystem.path.join(parent.path, 'without_entitlements.txt');
- if (!(await fileSystem.file(entitlementFilePath).exists())) {
- log.warning('$entitlementFilePath not found. '
- 'by default, system will assume there is no ${entitlements ? '' : 'without_'}entitlements file. '
- 'As a result, no binary will be codesigned.'
- 'if this is not intended, please provide them along with the engine artifacts.');
- return <String>{};
- }
-
- final Set<String> fileWithEntitlements = <String>{};
-
- fileWithEntitlements.addAll(await fileSystem.file(entitlementFilePath).readAsLines());
- // TODO(xilaizhang) : add back metadata information after https://github.com/flutter/flutter/issues/126705
- // is resolved.
- await fileSystem.file(entitlementFilePath).delete();
-
- return fileWithEntitlements;
- }
-
- /// Upload a zip archive to the notary service and verify the build succeeded.
- ///
- /// The apple notarization service will unzip the artifact, validate all
- /// binaries are properly codesigned, and notarize the entire archive.
- Future<void> notarize(File file) async {
- final Completer<void> completer = Completer<void>();
- final String uuid = uploadZipToNotary(file);
-
- Future<void> callback(Timer timer) async {
- final bool notaryFinished = checkNotaryJobFinished(uuid);
- if (notaryFinished) {
- timer.cancel();
- log.info('successfully notarized ${file.path}');
- completer.complete();
- }
- }
-
- // check on results
- Timer.periodic(
- notarizationTimerDuration,
- callback,
- );
- await completer.future;
- }
-
- /// Make a request to the notary service to see if the notary job is finished.
- ///
- /// A return value of true means that notarization finished successfully,
- /// false means that the job is still pending. If the notarization fails, this
- /// function will throw a [ConductorException].
- bool checkNotaryJobFinished(String uuid) {
- final List<String> args = <String>[
- 'xcrun',
- 'notarytool',
- 'info',
- uuid,
- '--password',
- appSpecificPassword,
- '--apple-id',
- codesignAppstoreId,
- '--team-id',
- codesignTeamId,
- ];
-
- log.info('checking notary status with ${args.join(' ')}');
- final io.ProcessResult result = processManager.runSync(args);
- final String combinedOutput = (result.stdout as String) + (result.stderr as String);
-
- final RegExpMatch? match = _notarytoolStatusCheckPattern.firstMatch(combinedOutput);
-
- if (match == null) {
- throw CodesignException(
- 'Malformed output from "${args.join(' ')}"\n${combinedOutput.trim()}',
- );
- }
-
- final String status = match.group(1)!;
-
- if (status == 'Accepted') {
- return true;
- }
- if (status == 'In Progress') {
- log.info('job $uuid still pending');
- return false;
- }
- throw CodesignException('Notarization failed with: $status\n$combinedOutput');
- }
-
- /// Upload artifact to Apple notary service.
- String uploadZipToNotary(File localFile, [int retryCount = 3, int sleepTime = 1]) {
- while (retryCount > 0) {
- final List<String> args = <String>[
- 'xcrun',
- 'notarytool',
- 'submit',
- localFile.absolute.path,
- '--apple-id',
- codesignAppstoreId,
- '--password',
- appSpecificPassword,
- '--team-id',
- codesignTeamId,
- ];
-
- log.info('uploading ${args.join(' ')}');
- final io.ProcessResult result = processManager.runSync(args);
- if (result.exitCode != 0) {
- throw CodesignException(
- 'Command "${args.join(' ')}" failed with exit code ${result.exitCode}\nStdout: ${result.stdout}\nStderr: ${result.stderr}',
- );
- }
-
- final String combinedOutput = (result.stdout as String) + (result.stderr as String);
- final RegExpMatch? match;
- match = _notarytoolRequestPattern.firstMatch(combinedOutput);
-
- if (match == null) {
- log.warning('Failed to upload to the notary service with args: ${args.join(' ')}');
- log.warning('{combinedOutput.trim()}');
- retryCount -= 1;
- log.warning('Trying again $retryCount more time${retryCount > 1 ? 's' : ''}...');
- io.sleep(Duration(seconds: sleepTime));
- continue;
- }
-
- final String requestUuid = match.group(1)!;
- log.info('RequestUUID for ${localFile.path} is: $requestUuid');
-
- return requestUuid;
- }
- log.warning('The upload to notary service failed after retries, and'
- ' the output format does not match the current notary tool version.'
- ' If after inspecting the output, you believe the process finished '
- 'successfully but was not detected, please contact flutter release engineers');
- throw CodesignException('Failed to upload ${localFile.path} to the notary service');
- }
-}
diff --git a/cipd_packages/codesign/lib/src/log.dart b/cipd_packages/codesign/lib/src/log.dart
deleted file mode 100644
index 6c5e18d..0000000
--- a/cipd_packages/codesign/lib/src/log.dart
+++ /dev/null
@@ -1,7 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:logging/logging.dart';
-
-final Logger log = Logger('codesign');
diff --git a/cipd_packages/codesign/lib/src/utils.dart b/cipd_packages/codesign/lib/src/utils.dart
deleted file mode 100644
index 0d30e29..0000000
--- a/cipd_packages/codesign/lib/src/utils.dart
+++ /dev/null
@@ -1,117 +0,0 @@
-// Copyright 2019 The Flutter 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:args/args.dart';
-import 'package:file/file.dart';
-import 'package:process/process.dart';
-
-import 'log.dart';
-
-enum FileType {
- folder,
- zip,
- binary,
- other;
-
- // Artifact files have many different types. Codesign would be no-op when encoutering non typical types such as application/octet-stream.
- factory FileType.fromMimeType(String mimeType) {
- if (mimeType.contains('inode/directory')) {
- return FileType.folder;
- } else if (mimeType.contains('application/zip')) {
- return FileType.zip;
- } else if (mimeType.contains('application/x-mach-binary')) {
- return FileType.binary;
- } else {
- return FileType.other;
- }
- }
-}
-
-Future<void> unzip({
- required FileSystemEntity inputZip,
- required Directory outDir,
- required ProcessManager processManager,
-}) async {
- await processManager.run(
- <String>[
- 'unzip',
- inputZip.absolute.path,
- '-d',
- outDir.absolute.path,
- ],
- );
- log.info('The downloaded file is unzipped from ${inputZip.absolute.path} to ${outDir.absolute.path}');
-}
-
-Future<void> zip({
- required Directory inputDir,
- required String outputZipPath,
- required ProcessManager processManager,
-}) async {
- await processManager.run(
- <String>[
- 'zip',
- '--symlinks',
- '--recurse-paths',
- outputZipPath,
- // use '.' so that the full absolute path is not encoded into the zip file
- '.',
- '--include',
- '*',
- ],
- workingDirectory: inputDir.absolute.path,
- );
-}
-
-/// Check mime-type of file at [filePath] to determine if it is a directory.
-FileType getFileType(String filePath, ProcessManager processManager) {
- final ProcessResult result = processManager.runSync(
- <String>[
- 'file',
- '--mime-type',
- '-b', // is binary
- filePath,
- ],
- );
- final String output = result.stdout as String;
- return FileType.fromMimeType(output);
-}
-
-class CodesignException implements Exception {
- CodesignException(this.message);
-
- final String message;
-
- @override
- String toString() => 'Exception: $message';
-}
-
-/// Return the command line argument by parsing [argResults].
-///
-/// If the key does not exist in CLI args, throws a [CodesignException].
-String? getValueFromArgs(
- String name,
- ArgResults argResults, {
- bool allowNull = false,
-}) {
- final String? argValue = argResults[name] as String?;
- if (argValue != null) {
- return argValue;
- }
- if (allowNull) {
- return null;
- }
- throw CodesignException('Expected either the CLI arg --$name '
- 'to be provided!');
-}
-
-String joinEntitlementPaths(String entitlementParentPath, String pathToJoin) {
- if (entitlementParentPath == '') {
- return pathToJoin;
- } else {
- return '$entitlementParentPath/$pathToJoin';
- }
-}
diff --git a/cipd_packages/codesign/lib/verify.dart b/cipd_packages/codesign/lib/verify.dart
deleted file mode 100644
index 15c3d22..0000000
--- a/cipd_packages/codesign/lib/verify.dart
+++ /dev/null
@@ -1,185 +0,0 @@
-// Copyright 2019 The Flutter 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' as io;
-
-import 'package:file/file.dart';
-import 'package:file/local.dart';
-import 'package:logging/logging.dart';
-import 'package:process/process.dart';
-
-const String kNotNotarizedMessage = 'test-requirement: code failed to satisfy specified code requirement(s)';
-
-enum VerificationResult {
- unsigned,
- codesignedOnly,
- codesignedAndNotarized,
-}
-
-class VerificationService {
- VerificationService({
- required this.binaryPath,
- required this.logger,
- this.fs = const LocalFileSystem(),
- this.pm = const LocalProcessManager(),
- }) {
- if (!fs.file(binaryPath).existsSync()) {
- throw Exception(
- 'Input file `$binaryPath` does not exist--please provide the path to a '
- 'valid binary to verify.',
- );
- }
- if (!pm.canRun('codesign')) {
- throw Exception(
- 'The binary `codesign` is required to run this tool. Do you have '
- 'Xcode installed?',
- );
- }
- }
-
- final Logger logger;
- final String binaryPath;
- final FileSystem fs;
- final ProcessManager pm;
-
- late final String _codesignTimestamp;
- late final String _format;
- late final String _signatureSize;
- late final String _codesignId;
- bool? _notarizationStatus;
-
- Future<VerificationResult> run() async {
- if (!await _codesignDisplay()) {
- return VerificationResult.unsigned;
- }
- await _notarization();
- logger.info(present());
- return _notarizationStatus! ? VerificationResult.codesignedAndNotarized : VerificationResult.codesignedOnly;
- }
-
- Future<void> _notarization() async {
- final List<String> command = <String>[
- 'codesign',
- '--verify',
- '-v',
- // force online notarization check
- '-R=notarized',
- '--check-notarization',
- binaryPath,
- ];
- final io.ProcessResult result = await pm.run(command);
-
- // This usually means it does not satisfy notarization requirement
- if (result.exitCode == 3 && (result.stderr as String).contains(kNotNotarizedMessage)) {
- _notarizationStatus = false;
- return;
- }
- if (result.exitCode != 0) {
- throw Exception('''
-Command `${command.join(' ')}` failed with code ${result.exitCode}
-
-${result.stderr}
-''');
- }
- final String stderr = result.stderr as String;
- if (stderr.contains('explicit requirement satisfied')) {
- _notarizationStatus = true;
- return;
- }
- throw UnimplementedError('Failed parsing the output of `${command.join(' ')}`:\n\n$stderr');
- }
-
- String present() {
- return '''
-Authority: $_codesignId
-Time stamp: $_codesignTimestamp
-Format: $_format
-Signature size: $_signatureSize
-Notarization: $_notarizationStatus
-''';
- }
-
- /// Display overall information, intended to be machine parseable
- ///
- /// Output is of the format:
- ///
- /// Executable=/Users/developer/Downloads/mybinary
- /// Identifier=mybinary
- /// Format=Mach-O thin (x86_64)
- /// CodeDirectory v=20500 size=38000 flags=0x10000(runtime) hashes=1177+7 location=embedded
- /// Signature size=8979
- /// Authority=Developer ID Application: Dev Shop ABC (ABCC0VV123)
- /// Authority=Developer ID Certification Authority
- /// Authority=Apple Root CA
- /// Timestamp=Jan 9, 2023 at 9:39:07 AM
- /// Info.plist=not bound
- /// TeamIdentifier=ABCC0VV123
- /// Runtime Version=13.1.0
- /// Sealed Resources=none
- /// Internal requirements count=1 size=164
- Future<bool> _codesignDisplay() async {
- final List<String> command = <String>[
- 'codesign',
- '--display',
- '-vv',
- binaryPath,
- ];
- final io.ProcessResult result = await pm.run(command);
-
- if (result.exitCode == 1) {
- logger.severe('''
-File $binaryPath is not codesigned. To manually verify, run:
-
-codesign --display -vv $binaryPath
-''');
- return false;
- } else if (result.exitCode != 0) {
- throw Exception(
- 'Command `${command.join(' ')}` failed with code ${result.exitCode}\n\n'
- '${result.stderr}',
- );
- }
-
- final List<String> lines = result.stderr.toString().trim().split('\n');
- for (final String line in lines) {
- if (line.trim().isEmpty) {
- continue;
- }
- final List<String> segments = line.split('=');
- final String name = segments.first;
-
- switch (name) {
- case 'Executable':
- case 'Identifier':
- case 'CodeDirectory v':
- case 'Info.plist':
- // TeamIdentifier is redundant with the Authority field
- case 'TeamIdentifier':
- case 'Runtime Version':
- case 'Sealed Resources':
- case 'Internal requirements count':
- break;
- case 'Signature size':
- _signatureSize = segments.sublist(1).join();
- break;
- case 'Authority':
- if (segments[1].startsWith('Developer ID Application')) {
- _codesignId = segments[1];
- }
- break;
- case 'Timestamp':
- _codesignTimestamp = segments[1];
- break;
- case 'Format':
- _format = segments.sublist(1).join();
- break;
- default:
- logger.warning(
- 'Do not know how to parse a $name, skipping this field.',
- );
- }
- }
- return true;
- }
-}
diff --git a/cipd_packages/codesign/pubspec.yaml b/cipd_packages/codesign/pubspec.yaml
deleted file mode 100644
index c2e12d2..0000000
--- a/cipd_packages/codesign/pubspec.yaml
+++ /dev/null
@@ -1,22 +0,0 @@
-name: codesign
-description: A standalone app to codesign Mac engine binaries.
-version: 1.0.0
-homepage: https://github.com/flutter/cocoon
-
-environment:
- sdk: '>=2.18.0 <4.0.0'
-
-dev_dependencies:
- lints: 3.0.0
- test: 1.24.9
-dependencies:
- archive: 3.4.9
- args: 2.4.2
- crypto: 3.0.3
- fake_async: 1.3.1
- file: 7.0.0
- flutter_lints: 3.0.1
- logging: 1.2.0
- meta: 1.11.0
- platform: 3.1.3
- process: 5.0.1
diff --git a/cipd_packages/codesign/test/file_codesign_visitor_test.dart b/cipd_packages/codesign/test/file_codesign_visitor_test.dart
deleted file mode 100644
index 0b5e87b..0000000
--- a/cipd_packages/codesign/test/file_codesign_visitor_test.dart
+++ /dev/null
@@ -1,1249 +0,0 @@
-// Copyright 2019 The Flutter 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:convert';
-
-import 'package:codesign/codesign.dart' as cs;
-import 'package:codesign/src/log.dart';
-import 'package:codesign/src/utils.dart';
-import 'package:file/file.dart';
-import 'package:file/memory.dart';
-import 'package:logging/logging.dart';
-import 'package:test/test.dart';
-
-import './src/fake_process_manager.dart';
-
-void main() {
- const String randomString = 'abcd1234';
- const String appSpecificPasswordFilePath = '/tmp/passwords.txt';
- const String codesignAppstoreIDFilePath = '/tmp/appID.txt';
- const String codesignTeamIDFilePath = '/tmp/teamID.txt';
- const String inputZipPath = '/tmp/input.zip';
- const String outputZipPath = '/tmp/output.zip';
- final List<LogRecord> records = <LogRecord>[];
-
- late MemoryFileSystem fileSystem;
- late FakeProcessManager processManager;
- late cs.FileCodesignVisitor codesignVisitor;
- late Directory rootDirectory;
-
- setUp(() {
- fileSystem = MemoryFileSystem.test();
- rootDirectory = fileSystem.systemTempDirectory.createTempSync('conductor_codesign');
- processManager = FakeProcessManager.list(<FakeCommand>[]);
- records.clear();
- log.onRecord.listen((LogRecord record) => records.add(record));
- });
-
- group('test reading in passwords: ', () {
- setUp(() {
- codesignVisitor = cs.FileCodesignVisitor(
- codesignCertName: randomString,
- fileSystem: fileSystem,
- appSpecificPasswordFilePath: appSpecificPasswordFilePath,
- codesignAppstoreIDFilePath: codesignAppstoreIDFilePath,
- codesignTeamIDFilePath: codesignTeamIDFilePath,
- processManager: processManager,
- rootDirectory: rootDirectory,
- inputZipPath: inputZipPath,
- outputZipPath: outputZipPath,
- notarizationTimerDuration: const Duration(seconds: 0),
- dryrun: false,
- );
- codesignVisitor.directoriesVisited.clear();
- });
-
- test('lacking password file throws exception', () async {
- expect(
- () async {
- await codesignVisitor.readPassword(appSpecificPasswordFilePath);
- },
- throwsA(
- isA<CodesignException>(),
- ),
- );
- });
-
- test('providing correctly formatted password returns normally', () async {
- fileSystem.file(appSpecificPasswordFilePath)
- ..createSync(recursive: true, exclusive: true)
- ..writeAsStringSync(
- '123',
- mode: FileMode.write,
- encoding: utf8,
- );
-
- expect(
- () async {
- await codesignVisitor.readPassword(appSpecificPasswordFilePath);
- await fileSystem.file(appSpecificPasswordFilePath).delete();
- },
- returnsNormally,
- );
- });
- });
-
- group('test utils function to join virtual entitlement path: ', () {
- test('omits slash for the first path', () async {
- expect(joinEntitlementPaths('', randomString), randomString);
- });
-
- test('concat with slash', () async {
- expect(joinEntitlementPaths(randomString, randomString), '$randomString/$randomString');
- });
- });
-
- group('test google cloud storage and processRemoteZip workflow', () {
- setUp(() {
- codesignVisitor = cs.FileCodesignVisitor(
- codesignCertName: randomString,
- fileSystem: fileSystem,
- appSpecificPasswordFilePath: appSpecificPasswordFilePath,
- codesignAppstoreIDFilePath: codesignAppstoreIDFilePath,
- codesignTeamIDFilePath: codesignTeamIDFilePath,
- processManager: processManager,
- rootDirectory: rootDirectory,
- notarizationTimerDuration: const Duration(seconds: 0),
- dryrun: false,
- inputZipPath: inputZipPath,
- outputZipPath: outputZipPath,
- );
- codesignVisitor.directoriesVisited.clear();
- codesignVisitor.appSpecificPassword = randomString;
- codesignVisitor.codesignAppstoreId = randomString;
- codesignVisitor.codesignTeamId = randomString;
- });
-
- test('procesRemotezip triggers correct workflow', () async {
- final String zipFileName = '${rootDirectory.path}/remote_zip_4/folder_1/zip_1';
- fileSystem.file(zipFileName).createSync(recursive: true);
- processManager.addCommands(<FakeCommand>[
- FakeCommand(
- command: <String>[
- 'unzip',
- codesignVisitor.inputZipPath,
- '-d',
- '${rootDirectory.absolute.path}/single_artifact',
- ],
- onRun: () => fileSystem
- ..file('${rootDirectory.path}/single_artifact/entitlements.txt').createSync(recursive: true)
- ..file('${rootDirectory.path}/single_artifact/without_entitlements.txt').createSync(recursive: true),
- ),
- FakeCommand(
- command: <String>[
- 'zip',
- '--symlinks',
- '--recurse-paths',
- codesignVisitor.outputZipPath,
- '.',
- '--include',
- '*',
- ],
- ),
- FakeCommand(
- command: <String>[
- 'xcrun',
- 'notarytool',
- 'submit',
- codesignVisitor.outputZipPath,
- '--apple-id',
- randomString,
- '--password',
- randomString,
- '--team-id',
- randomString,
- ],
- stdout: 'id: $randomString',
- ),
- const FakeCommand(
- command: <String>[
- 'xcrun',
- 'notarytool',
- 'info',
- randomString,
- '--password',
- randomString,
- '--apple-id',
- randomString,
- '--team-id',
- randomString,
- ],
- stdout: 'status: Accepted',
- ),
- ]);
-
- await codesignVisitor.processRemoteZip();
- final Set<String> messages = records
- .where((LogRecord record) => record.level == Level.INFO)
- .map((LogRecord record) => record.message)
- .toSet();
- expect(
- messages,
- contains(
- 'The downloaded file is unzipped from ${codesignVisitor.inputZipPath} to ${rootDirectory.path}/single_artifact',
- ),
- );
- expect(
- messages,
- contains('Visiting directory ${rootDirectory.absolute.path}/single_artifact'),
- );
- expect(
- messages,
- contains('parsed binaries with entitlements are {}'),
- );
- expect(
- messages,
- contains('parsed binaries without entitlements are {}'),
- );
- expect(
- messages,
- contains(
- 'uploading xcrun notarytool submit ${codesignVisitor.outputZipPath} --apple-id $randomString --password $randomString --team-id $randomString',
- ),
- );
- expect(
- messages,
- contains('RequestUUID for ${codesignVisitor.outputZipPath} is: $randomString'),
- );
- expect(
- messages,
- contains(
- 'checking notary status with xcrun notarytool info $randomString --password $randomString --apple-id $randomString --team-id $randomString',
- ),
- );
- expect(
- messages,
- contains('successfully notarized ${codesignVisitor.outputZipPath}'),
- );
- });
- });
-
- group('visit directory/zip api calls: ', () {
- setUp(() {
- codesignVisitor = cs.FileCodesignVisitor(
- codesignCertName: randomString,
- fileSystem: fileSystem,
- appSpecificPasswordFilePath: appSpecificPasswordFilePath,
- codesignAppstoreIDFilePath: codesignAppstoreIDFilePath,
- codesignTeamIDFilePath: codesignTeamIDFilePath,
- processManager: processManager,
- rootDirectory: rootDirectory,
- inputZipPath: inputZipPath,
- outputZipPath: outputZipPath,
- notarizationTimerDuration: Duration.zero,
- );
- codesignVisitor.directoriesVisited.clear();
- codesignVisitor.appSpecificPassword = randomString;
- codesignVisitor.codesignAppstoreId = randomString;
- codesignVisitor.codesignTeamId = randomString;
- });
-
- test('visitDirectory correctly list files', () async {
- fileSystem
- ..file('${rootDirectory.path}/remote_zip_0/file_a').createSync(recursive: true)
- ..file('${rootDirectory.path}/remote_zip_0/file_b').createSync(recursive: true)
- ..file('${rootDirectory.path}/remote_zip_0/file_c').createSync(recursive: true);
- processManager.addCommands(<FakeCommand>[
- FakeCommand(
- command: <String>[
- 'file',
- '--mime-type',
- '-b',
- '${rootDirectory.absolute.path}/remote_zip_0/file_a',
- ],
- stdout: 'other_files',
- ),
- FakeCommand(
- command: <String>[
- 'file',
- '--mime-type',
- '-b',
- '${rootDirectory.absolute.path}/remote_zip_0/file_b',
- ],
- stdout: 'other_files',
- ),
- FakeCommand(
- command: <String>[
- 'file',
- '--mime-type',
- '-b',
- '${rootDirectory.absolute.path}/remote_zip_0/file_c',
- ],
- stdout: 'other_files',
- ),
- ]);
- final Directory testDirectory = fileSystem.directory('${rootDirectory.path}/remote_zip_0');
- await codesignVisitor.visitDirectory(
- directory: testDirectory,
- parentVirtualPath: 'a.zip',
- );
- final List<String> messages = records
- .where((LogRecord record) => record.level == Level.INFO)
- .map((LogRecord record) => record.message)
- .toList();
- expect(messages, contains('Visiting directory ${rootDirectory.path}/remote_zip_0'));
- expect(messages, contains('Child file of directory remote_zip_0 is file_a'));
- expect(messages, contains('Child file of directory remote_zip_0 is file_b'));
- expect(messages, contains('Child file of directory remote_zip_0 is file_c'));
- });
-
- test('visitDirectory recursively visits directory', () async {
- fileSystem
- ..file('${rootDirectory.path}/remote_zip_1/file_a').createSync(recursive: true)
- ..file('${rootDirectory.path}/remote_zip_1/folder_a/file_b').createSync(recursive: true);
- final Directory testDirectory = fileSystem.directory('${rootDirectory.path}/remote_zip_1');
- processManager.addCommands(<FakeCommand>[
- FakeCommand(
- command: <String>[
- 'file',
- '--mime-type',
- '-b',
- '${rootDirectory.absolute.path}/remote_zip_1/file_a',
- ],
- stdout: 'other_files',
- ),
- FakeCommand(
- command: <String>[
- 'file',
- '--mime-type',
- '-b',
- '${rootDirectory.absolute.path}/remote_zip_1/folder_a/file_b',
- ],
- stdout: 'other_files',
- ),
- ]);
- await codesignVisitor.visitDirectory(
- directory: testDirectory,
- parentVirtualPath: '',
- );
- final List<String> messages = records
- .where((LogRecord record) => record.level == Level.INFO)
- .map((LogRecord record) => record.message)
- .toList();
- expect(messages, contains('Visiting directory ${rootDirectory.path}/remote_zip_1'));
- expect(messages, contains('Visiting directory ${rootDirectory.path}/remote_zip_1/folder_a'));
- expect(messages, contains('Child file of directory remote_zip_1 is file_a'));
- expect(messages, contains('Child file of directory folder_a is file_b'));
- });
-
- test('visit directory inside a zip', () async {
- final String zipFileName = '${rootDirectory.path}/remote_zip_2/zip_1';
- fileSystem.file(zipFileName).createSync(recursive: true);
- processManager.addCommands(<FakeCommand>[
- FakeCommand(
- command: <String>[
- 'unzip',
- '${rootDirectory.absolute.path}/remote_zip_2/zip_1',
- '-d',
- '${rootDirectory.absolute.path}/embedded_zip_${zipFileName.hashCode}',
- ],
- onRun: () => fileSystem
- ..file('${rootDirectory.path}/embedded_zip_${zipFileName.hashCode}/file_1').createSync(recursive: true)
- ..file('${rootDirectory.path}/embedded_zip_${zipFileName.hashCode}/file_2').createSync(recursive: true),
- ),
- FakeCommand(
- command: <String>[
- 'file',
- '--mime-type',
- '-b',
- '${rootDirectory.absolute.path}/embedded_zip_${zipFileName.hashCode}/file_1',
- ],
- stdout: 'other_files',
- ),
- FakeCommand(
- command: <String>[
- 'file',
- '--mime-type',
- '-b',
- '${rootDirectory.absolute.path}/embedded_zip_${zipFileName.hashCode}/file_2',
- ],
- stdout: 'other_files',
- ),
- FakeCommand(
- command: <String>[
- 'zip',
- '--symlinks',
- '--recurse-paths',
- '${rootDirectory.absolute.path}/remote_zip_2/zip_1',
- '.',
- '--include',
- '*',
- ],
- onRun: () => fileSystem.file('${rootDirectory.path}/remote_zip_2/zip_1').createSync(recursive: true),
- ),
- ]);
-
- await codesignVisitor.visitEmbeddedZip(
- zipEntity: fileSystem.file('${rootDirectory.path}/remote_zip_2/zip_1'),
- parentVirtualPath: 'a.zip',
- );
- final List<String> messages = records
- .where((LogRecord record) => record.level == Level.INFO)
- .map((LogRecord record) => record.message)
- .toList();
- expect(
- messages,
- contains(
- 'The downloaded file is unzipped from ${rootDirectory.path}/remote_zip_2/zip_1 to ${rootDirectory.path}/embedded_zip_${zipFileName.hashCode}',
- ),
- );
- expect(messages, contains('Visiting directory ${rootDirectory.path}/embedded_zip_${zipFileName.hashCode}'));
- expect(messages, contains('Child file of directory embedded_zip_${zipFileName.hashCode} is file_1'));
- expect(messages, contains('Child file of directory embedded_zip_${zipFileName.hashCode} is file_2'));
- });
-
- test('visit zip inside a directory', () async {
- final String zipFileName = '${rootDirectory.path}/remote_zip_4/folder_1/zip_1';
- fileSystem.file(zipFileName).createSync(recursive: true);
- processManager.addCommands(<FakeCommand>[
- FakeCommand(
- command: <String>[
- 'file',
- '--mime-type',
- '-b',
- '${rootDirectory.absolute.path}/remote_zip_4/folder_1/zip_1',
- ],
- stdout: 'application/zip',
- ),
- FakeCommand(
- command: <String>[
- 'unzip',
- '${rootDirectory.absolute.path}/remote_zip_4/folder_1/zip_1',
- '-d',
- '${rootDirectory.absolute.path}/embedded_zip_${zipFileName.hashCode}',
- ],
- onRun: () => fileSystem
- .directory('${rootDirectory.path}/embedded_zip_${zipFileName.hashCode}')
- .createSync(recursive: true),
- ),
- FakeCommand(
- command: <String>[
- 'zip',
- '--symlinks',
- '--recurse-paths',
- '${rootDirectory.absolute.path}/remote_zip_4/folder_1/zip_1',
- '.',
- '--include',
- '*',
- ],
- ),
- ]);
-
- await codesignVisitor.visitDirectory(
- directory: fileSystem.directory('${rootDirectory.path}/remote_zip_4'),
- parentVirtualPath: 'a.zip',
- );
- final List<String> messages = records
- .where((LogRecord record) => record.level == Level.INFO)
- .map((LogRecord record) => record.message)
- .toList();
- expect(messages, contains('Visiting directory ${rootDirectory.absolute.path}/remote_zip_4'));
- expect(messages, contains('Visiting directory ${rootDirectory.absolute.path}/remote_zip_4/folder_1'));
- expect(
- messages,
- contains(
- 'The downloaded file is unzipped from ${rootDirectory.path}/remote_zip_4/folder_1/zip_1 to ${rootDirectory.path}/embedded_zip_${zipFileName.hashCode}',
- ),
- );
- expect(
- messages,
- contains('Visiting directory ${rootDirectory.absolute.path}/embedded_zip_${zipFileName.hashCode}'),
- );
- });
-
- test('throw exception when the same directory is visited', () async {
- fileSystem.file('${rootDirectory.path}/parent_1/child_1/file_1').createSync(recursive: true);
- processManager.addCommands(<FakeCommand>[
- FakeCommand(
- command: <String>[
- 'file',
- '--mime-type',
- '-b',
- '${rootDirectory.absolute.path}/parent_1/child_1/file_1',
- ],
- stdout: 'other_files',
- ),
- FakeCommand(
- command: <String>[
- 'file',
- '--mime-type',
- '-b',
- '${rootDirectory.absolute.path}/parent_1/child_1/file_1',
- ],
- stdout: 'other_files',
- ),
- ]);
-
- await codesignVisitor.visitDirectory(
- directory: fileSystem.directory('${rootDirectory.path}/parent_1/child_1'),
- parentVirtualPath: 'a.zip',
- );
- List<String> warnings = records
- .where((LogRecord record) => record.level == Level.WARNING)
- .map((LogRecord record) => record.message)
- .toList();
- expect(warnings, isEmpty);
-
- await codesignVisitor.visitDirectory(
- directory: fileSystem.directory('${rootDirectory.path}/parent_1'),
- parentVirtualPath: 'a.zip',
- );
- warnings = records
- .where((LogRecord record) => record.level == Level.WARNING)
- .map((LogRecord record) => record.message)
- .toList();
- expect(
- warnings,
- contains(
- 'Warning! You are visiting a directory that has been visited before, the directory is ${rootDirectory.path}/parent_1/child_1',
- ),
- );
- });
-
- test('visitDirectory skips file or directory that is a symlink', () async {
- fileSystem
- ..file('${rootDirectory.path}/remote_zip_5/target_dir/file_b').createSync(recursive: true)
- ..directory('${rootDirectory.path}/remote_zip_5/symlink_dir').createSync(recursive: true)
- ..link('${rootDirectory.path}/remote_zip_5/symlink_dir/file_a')
- .createSync('${rootDirectory.path}/remote_zip_5/target_dir/file_b')
- ..link('${rootDirectory.path}/remote_zip_5/symlink_dir_2')
- .createSync('${rootDirectory.path}/remote_zip_5/target_dir');
- processManager.addCommands(<FakeCommand>[
- FakeCommand(
- command: <String>[
- 'file',
- '--mime-type',
- '-b',
- '${rootDirectory.absolute.path}/remote_zip_5/target_dir/file_b',
- ],
- stdout: 'other_files',
- ),
- ]);
- final Directory testDirectory = fileSystem.directory('${rootDirectory.path}/remote_zip_5');
- await codesignVisitor.visitDirectory(
- directory: testDirectory,
- parentVirtualPath: 'a.zip',
- );
- final Set<String> messages = records
- .where((LogRecord record) => record.level == Level.INFO)
- .map((LogRecord record) => record.message)
- .toSet();
- expect(messages, contains('Visiting directory ${rootDirectory.path}/remote_zip_5/target_dir'));
- expect(messages, contains('Child file of directory target_dir is file_b'));
-
- // Skip code signing a file that is a symlink.
- expect(messages, contains('Visiting directory ${rootDirectory.path}/remote_zip_5/symlink_dir'));
- expect(
- messages,
- contains('current file or direcotry ${rootDirectory.path}/remote_zip_5/symlink_dir/file_a is a symlink to '
- '${rootDirectory.path}/remote_zip_5/target_dir/file_b, codesign is therefore skipped for the current file or directory.'),
- );
- expect(messages, isNot(contains('Child file of directory symlink_dir is file_a')));
-
- // Skip code signing a directory that is a symlink.
- expect(
- messages,
- contains('current file or direcotry ${rootDirectory.path}/remote_zip_5/symlink_dir_2 is a symlink to '
- '${rootDirectory.path}/remote_zip_5/target_dir, codesign is therefore skipped for the current file or directory.'),
- );
- });
-
- test('visitBinary codesigns binary with / without entitlement', () async {
- codesignVisitor = cs.FileCodesignVisitor(
- codesignCertName: randomString,
- fileSystem: fileSystem,
- appSpecificPasswordFilePath: appSpecificPasswordFilePath,
- codesignAppstoreIDFilePath: codesignAppstoreIDFilePath,
- codesignTeamIDFilePath: codesignTeamIDFilePath,
- processManager: processManager,
- rootDirectory: rootDirectory,
- inputZipPath: inputZipPath,
- outputZipPath: outputZipPath,
- dryrun: false,
- notarizationTimerDuration: const Duration(seconds: 0),
- );
- codesignVisitor.appSpecificPassword = randomString;
- codesignVisitor.codesignAppstoreId = randomString;
- codesignVisitor.codesignTeamId = randomString;
- codesignVisitor.fileWithEntitlements = <String>{'root/folder_a/file_a'};
- codesignVisitor.fileWithoutEntitlements = <String>{'root/folder_b/file_b'};
- fileSystem
- ..file('${rootDirectory.path}/remote_zip_6/folder_a/file_a').createSync(recursive: true)
- ..file('${rootDirectory.path}/remote_zip_6/folder_b/file_b').createSync(recursive: true);
- final Directory testDirectory = fileSystem.directory('${rootDirectory.path}/remote_zip_6');
- processManager.addCommands(<FakeCommand>[
- FakeCommand(
- command: <String>[
- 'file',
- '--mime-type',
- '-b',
- '${rootDirectory.absolute.path}/remote_zip_6/folder_a/file_a',
- ],
- stdout: 'application/x-mach-binary',
- ),
- FakeCommand(
- command: <String>[
- '/usr/bin/codesign',
- '--keychain',
- 'build.keychain',
- '-f',
- '-s',
- randomString,
- '${rootDirectory.absolute.path}/remote_zip_6/folder_a/file_a',
- '--timestamp',
- '--options=runtime',
- '--entitlements',
- '${rootDirectory.absolute.path}/Entitlements.plist',
- ],
- ),
- FakeCommand(
- command: <String>[
- 'file',
- '--mime-type',
- '-b',
- '${rootDirectory.absolute.path}/remote_zip_6/folder_b/file_b',
- ],
- stdout: 'application/x-mach-binary',
- ),
- FakeCommand(
- command: <String>[
- '/usr/bin/codesign',
- '--keychain',
- 'build.keychain',
- '-f',
- '-s',
- randomString,
- '${rootDirectory.absolute.path}/remote_zip_6/folder_b/file_b',
- '--timestamp',
- '--options=runtime',
- ],
- ),
- ]);
- await codesignVisitor.visitDirectory(
- directory: testDirectory,
- parentVirtualPath: 'root',
- );
- final List<String> messages = records
- .where((LogRecord record) => record.level == Level.INFO)
- .map((LogRecord record) => record.message)
- .toList();
- expect(messages, contains('signing file at path ${rootDirectory.absolute.path}/remote_zip_6/folder_a/file_a'));
- expect(messages, contains('the virtual entitlement path associated with file is root/folder_a/file_a'));
- expect(messages, contains('the decision to sign with entitlement is true'));
-
- expect(messages, contains('signing file at path ${rootDirectory.absolute.path}/remote_zip_6/folder_b/file_b'));
- expect(messages, contains('the virtual entitlement path associated with file is root/folder_b/file_b'));
- expect(messages, contains('the decision to sign with entitlement is false'));
- });
- });
-
- group('parse entitlement configs: ', () {
- setUp(() {
- codesignVisitor = cs.FileCodesignVisitor(
- codesignCertName: randomString,
- inputZipPath: inputZipPath,
- outputZipPath: outputZipPath,
- fileSystem: fileSystem,
- appSpecificPasswordFilePath: appSpecificPasswordFilePath,
- codesignAppstoreIDFilePath: codesignAppstoreIDFilePath,
- codesignTeamIDFilePath: codesignTeamIDFilePath,
- processManager: processManager,
- rootDirectory: rootDirectory,
- );
- codesignVisitor.directoriesVisited.clear();
- codesignVisitor.appSpecificPassword = randomString;
- codesignVisitor.codesignAppstoreId = randomString;
- codesignVisitor.codesignTeamId = randomString;
- });
-
- test('correctly store file paths', () async {
- fileSystem.file('${rootDirectory.absolute.path}/test_entitlement/entitlements.txt')
- ..createSync(recursive: true)
- ..writeAsStringSync(
- '''file_a
-file_b
-file_c''',
- mode: FileMode.append,
- encoding: utf8,
- );
-
- fileSystem.file('${rootDirectory.absolute.path}/test_entitlement/without_entitlements.txt')
- ..createSync(recursive: true)
- ..writeAsStringSync(
- '''file_d
-file_e''',
- mode: FileMode.append,
- encoding: utf8,
- );
- final Set<String> fileWithEntitlements = await codesignVisitor.parseEntitlements(
- fileSystem.directory('${rootDirectory.absolute.path}/test_entitlement'),
- true,
- );
- final Set<String> fileWithoutEntitlements = await codesignVisitor.parseEntitlements(
- fileSystem.directory('${rootDirectory.absolute.path}/test_entitlement'),
- false,
- );
- expect(fileWithEntitlements.length, 3);
- expect(
- fileWithEntitlements,
- containsAll(<String>[
- 'file_a',
- 'file_b',
- 'file_c',
- ]),
- );
- expect(fileWithoutEntitlements.length, 2);
- expect(
- fileWithoutEntitlements,
- containsAll(<String>[
- 'file_d',
- 'file_e',
- ]),
- );
- });
-
- test('log warnings when configuration file is missing', () async {
- fileSystem.file('${rootDirectory.absolute.path}/test_entitlement_2/entitlements.txt')
- ..createSync(recursive: true)
- ..writeAsStringSync(
- '''file_a
-file_b
-file_c''',
- mode: FileMode.append,
- encoding: utf8,
- );
-
- final Set<String> fileWithEntitlements = await codesignVisitor.parseEntitlements(
- fileSystem.directory('${rootDirectory.absolute.path}/test_entitlement_2'),
- true,
- );
- expect(fileWithEntitlements.length, 3);
- expect(
- fileWithEntitlements,
- containsAll(<String>[
- 'file_a',
- 'file_b',
- 'file_c',
- ]),
- );
- await codesignVisitor.parseEntitlements(
- fileSystem.directory('${rootDirectory.absolute.path}/test_entitlement_2'),
- false,
- );
- final List<String> messages = records
- .where((LogRecord record) => record.level == Level.WARNING)
- .map((LogRecord record) => record.message)
- .toList();
- expect(
- messages,
- contains('${rootDirectory.absolute.path}/test_entitlement_2/without_entitlements.txt not found. '
- 'by default, system will assume there is no without_entitlements file. '
- 'As a result, no binary will be codesigned.'
- 'if this is not intended, please provide them along with the engine artifacts.'),
- );
- });
- });
-
- group('notarization tests: ', () {
- setUp(() {
- codesignVisitor = cs.FileCodesignVisitor(
- codesignCertName: randomString,
- inputZipPath: inputZipPath,
- outputZipPath: outputZipPath,
- fileSystem: fileSystem,
- appSpecificPasswordFilePath: appSpecificPasswordFilePath,
- codesignAppstoreIDFilePath: codesignAppstoreIDFilePath,
- codesignTeamIDFilePath: codesignTeamIDFilePath,
- processManager: processManager,
- rootDirectory: rootDirectory,
- );
- codesignVisitor.directoriesVisited.clear();
- codesignVisitor.appSpecificPassword = randomString;
- codesignVisitor.codesignAppstoreId = randomString;
- codesignVisitor.codesignTeamId = randomString;
- });
-
- test('successful notarization check returns true', () async {
- processManager.addCommands(<FakeCommand>[
- const FakeCommand(
- command: <String>[
- 'xcrun',
- 'notarytool',
- 'info',
- randomString,
- '--password',
- randomString,
- '--apple-id',
- randomString,
- '--team-id',
- randomString,
- ],
- stdout: '''createdDate: 2021-04-29T01:38:09.498Z
-id: 2efe2717-52ef-43a5-96dc-0797e4ca1041
-name: OvernightTextEditor_11.6.8.zip
-status: Accepted''',
- ),
- ]);
-
- expect(
- codesignVisitor.checkNotaryJobFinished(randomString),
- true,
- );
- });
-
- test('wrong format (such as altool) check throws exception', () async {
- processManager.addCommands(<FakeCommand>[
- const FakeCommand(
- command: <String>[
- 'xcrun',
- 'notarytool',
- 'info',
- randomString,
- '--password',
- randomString,
- '--apple-id',
- randomString,
- '--team-id',
- randomString,
- ],
- stdout: '''RequestUUID: 2EFE2717-52EF-43A5-96DC-0797E4CA1041
-Date: 2021-07-02 20:32:01 +0000
-Status: invalid
-LogFileURL: https://osxapps.itunes.apple.com/...
-Status Code: 2
-Status Message: Package Invalid''',
- ),
- ]);
-
- expect(
- () => codesignVisitor.checkNotaryJobFinished(randomString),
- throwsA(
- isA<CodesignException>(),
- ),
- );
- });
-
- test('in progress notarization check returns false', () async {
- processManager.addCommands(<FakeCommand>[
- const FakeCommand(
- command: <String>[
- 'xcrun',
- 'notarytool',
- 'info',
- randomString,
- '--password',
- randomString,
- '--apple-id',
- randomString,
- '--team-id',
- randomString,
- ],
- stdout: '''createdDate: 2021-04-29T01:38:09.498Z
-id: 2efe2717-52ef-43a5-96dc-0797e4ca1041
-name: OvernightTextEditor_11.6.8.zip
-status: In Progress''',
- ),
- ]);
-
- expect(
- codesignVisitor.checkNotaryJobFinished(randomString),
- false,
- );
- });
-
- test('invalid status check throws exception', () async {
- processManager.addCommands(<FakeCommand>[
- const FakeCommand(
- command: <String>[
- 'xcrun',
- 'notarytool',
- 'info',
- randomString,
- '--password',
- randomString,
- '--apple-id',
- randomString,
- '--team-id',
- randomString,
- ],
- stdout: '''createdDate: 2021-04-29T01:38:09.498Z
-id: 2efe2717-52ef-43a5-96dc-0797e4ca1041
-name: OvernightTextEditor_11.6.8.zip
-status: Invalid''',
- ),
- ]);
-
- expect(
- () => codesignVisitor.checkNotaryJobFinished(randomString),
- throwsA(
- isA<CodesignException>(),
- ),
- );
- });
-
- test('upload notary retries upon failure', () async {
- fileSystem.file('${rootDirectory.absolute.path}/temp').createSync();
- processManager.addCommands(<FakeCommand>[
- FakeCommand(
- command: <String>[
- 'xcrun',
- 'notarytool',
- 'submit',
- '${rootDirectory.absolute.path}/temp',
- '--apple-id',
- randomString,
- '--password',
- randomString,
- '--team-id',
- randomString,
- ],
- stdout: '''Error uploading file.
- Id: something that causes failure
- path: /Users/flutter/Desktop/OvernightTextEditor_11.6.8.zip''',
- ),
- FakeCommand(
- command: <String>[
- 'xcrun',
- 'notarytool',
- 'submit',
- '${rootDirectory.absolute.path}/temp',
- '--apple-id',
- randomString,
- '--password',
- randomString,
- '--team-id',
- randomString,
- ],
- stdout: '''Successfully uploaded file.
- id: 2efe2717-52ef-43a5-96dc-0797e4ca1041
- path: /Users/flutter/Desktop/OvernightTextEditor_11.6.8.zip''',
- ),
- ]);
-
- final String uuid = codesignVisitor.uploadZipToNotary(
- fileSystem.file('${rootDirectory.absolute.path}/temp'),
- 3,
- 0,
- );
- expect(uuid, '2efe2717-52ef-43a5-96dc-0797e4ca1041');
- final List<String> messages = records
- .where((LogRecord record) => record.level == Level.WARNING)
- .map((LogRecord record) => record.message)
- .toList();
- expect(
- messages,
- contains('Failed to upload to the notary service with args: '
- 'xcrun notarytool submit ${rootDirectory.absolute.path}/temp '
- '--apple-id abcd1234 --password abcd1234 --team-id abcd1234'),
- );
- expect(
- messages,
- contains('Trying again 2 more times...'),
- );
- });
-
- test('upload notary throws exception if exit code is unnormal', () async {
- fileSystem.file('${rootDirectory.absolute.path}/temp').createSync();
- processManager.addCommands(<FakeCommand>[
- FakeCommand(
- command: <String>[
- 'xcrun',
- 'notarytool',
- 'submit',
- '${rootDirectory.absolute.path}/temp',
- '--apple-id',
- randomString,
- '--password',
- randomString,
- '--team-id',
- randomString,
- ],
- stdout: '''Error uploading file.
- Id: something that causes failure
- path: /Users/flutter/Desktop/OvernightTextEditor_11.6.8.zip''',
- exitCode: -1,
- ),
- ]);
-
- expect(
- () => codesignVisitor.uploadZipToNotary(
- fileSystem.file('${rootDirectory.absolute.path}/temp'),
- 1,
- 0,
- ),
- throwsA(
- isA<CodesignException>(),
- ),
- );
- });
-
- test('upload notary throws exception after 3 default tries', () async {
- fileSystem.file('${rootDirectory.absolute.path}/temp').createSync();
- processManager.addCommands(<FakeCommand>[
- FakeCommand(
- command: <String>[
- 'xcrun',
- 'notarytool',
- 'submit',
- '${rootDirectory.absolute.path}/temp',
- '--apple-id',
- randomString,
- '--password',
- randomString,
- '--team-id',
- randomString,
- ],
- stdout: '''Error uploading file.
- Id: something that causes failure
- path: /Users/flutter/Desktop/OvernightTextEditor_11.6.8.zip''',
- ),
- FakeCommand(
- command: <String>[
- 'xcrun',
- 'notarytool',
- 'submit',
- '${rootDirectory.absolute.path}/temp',
- '--apple-id',
- randomString,
- '--password',
- randomString,
- '--team-id',
- randomString,
- ],
- stdout: '''Error uploading file.
- Id: something that causes failure
- path: /Users/flutter/Desktop/OvernightTextEditor_11.6.8.zip''',
- ),
- FakeCommand(
- command: <String>[
- 'xcrun',
- 'notarytool',
- 'submit',
- '${rootDirectory.absolute.path}/temp',
- '--apple-id',
- randomString,
- '--password',
- randomString,
- '--team-id',
- randomString,
- ],
- stdout: '''Error uploading file.
- Id: something that causes failure
- path: /Users/flutter/Desktop/OvernightTextEditor_11.6.8.zip''',
- ),
- ]);
-
- expect(
- () => codesignVisitor.uploadZipToNotary(
- fileSystem.file('${rootDirectory.absolute.path}/temp'),
- 3,
- 0,
- ),
- throwsA(
- isA<CodesignException>(),
- ),
- );
- final List<String> messages = records
- .where((LogRecord record) => record.level == Level.WARNING)
- .map((LogRecord record) => record.message)
- .toList();
- expect(
- messages,
- contains('The upload to notary service failed after retries, and'
- ' the output format does not match the current notary tool version.'
- ' If after inspecting the output, you believe the process finished '
- 'successfully but was not detected, please contact flutter release engineers'),
- );
- });
- });
-
- group('support optional switches and dryrun :', () {
- setUp(() {
- codesignVisitor = cs.FileCodesignVisitor(
- codesignCertName: randomString,
- inputZipPath: inputZipPath,
- outputZipPath: outputZipPath,
- fileSystem: fileSystem,
- appSpecificPasswordFilePath: appSpecificPasswordFilePath,
- codesignAppstoreIDFilePath: codesignAppstoreIDFilePath,
- codesignTeamIDFilePath: codesignTeamIDFilePath,
- processManager: processManager,
- rootDirectory: rootDirectory,
- notarizationTimerDuration: const Duration(seconds: 0),
- );
- codesignVisitor.directoriesVisited.clear();
- codesignVisitor.appSpecificPassword = randomString;
- codesignVisitor.codesignAppstoreId = randomString;
- codesignVisitor.codesignTeamId = randomString;
- fileSystem.file(codesignAppstoreIDFilePath)
- ..createSync(recursive: true)
- ..writeAsStringSync(randomString);
- fileSystem.file(codesignTeamIDFilePath)
- ..createSync(recursive: true)
- ..writeAsStringSync(randomString);
- fileSystem.file(appSpecificPasswordFilePath)
- ..createSync(recursive: true)
- ..writeAsStringSync(randomString);
- });
-
- test('codesign optional switches artifacts when dryrun is true', () async {
- processManager.addCommands(<FakeCommand>[
- FakeCommand(
- command: <String>[
- 'unzip',
- codesignVisitor.inputZipPath,
- '-d',
- '${rootDirectory.absolute.path}/single_artifact',
- ],
- onRun: () => fileSystem
- ..file('${rootDirectory.path}/single_artifact/entitlements.txt').createSync(recursive: true)
- ..file('${rootDirectory.path}/single_artifact/without_entitlements.txt').createSync(recursive: true),
- ),
- FakeCommand(
- command: <String>[
- 'zip',
- '--symlinks',
- '--recurse-paths',
- codesignVisitor.outputZipPath,
- '.',
- '--include',
- '*',
- ],
- ),
- FakeCommand(
- command: <String>[
- 'xcrun',
- 'notarytool',
- 'submit',
- codesignVisitor.outputZipPath,
- '--apple-id',
- randomString,
- '--password',
- randomString,
- '--team-id',
- randomString,
- ],
- stdout: 'id: $randomString',
- ),
- const FakeCommand(
- command: <String>[
- 'xcrun',
- 'notarytool',
- 'info',
- randomString,
- '--password',
- randomString,
- '--apple-id',
- randomString,
- '--team-id',
- randomString,
- ],
- stdout: 'status: Accepted',
- ),
- ]);
- await codesignVisitor.validateAll();
- final List<String> messages = records
- .where((LogRecord record) => record.level == Level.INFO)
- .map((LogRecord record) => record.message)
- .toList();
- expect(
- messages,
- contains('code signing dry run has completed, this is a quick sanity check without'
- 'going through the notary service. To run the full codesign process, use --no-dryrun flag.'),
- );
- });
-
- test('upload optional switch artifacts when dryrun is false', () async {
- processManager.addCommands(<FakeCommand>[
- FakeCommand(
- command: <String>[
- 'unzip',
- codesignVisitor.inputZipPath,
- '-d',
- '${rootDirectory.absolute.path}/single_artifact',
- ],
- onRun: () => fileSystem
- ..file('${rootDirectory.path}/single_artifact/entitlements.txt').createSync(recursive: true)
- ..file('${rootDirectory.path}/single_artifact/without_entitlements.txt').createSync(recursive: true),
- ),
- FakeCommand(
- command: <String>[
- 'zip',
- '--symlinks',
- '--recurse-paths',
- codesignVisitor.outputZipPath,
- '.',
- '--include',
- '*',
- ],
- ),
- FakeCommand(
- command: <String>[
- 'xcrun',
- 'notarytool',
- 'submit',
- codesignVisitor.outputZipPath,
- '--apple-id',
- randomString,
- '--password',
- randomString,
- '--team-id',
- randomString,
- ],
- stdout: 'id: $randomString',
- ),
- const FakeCommand(
- command: <String>[
- 'xcrun',
- 'notarytool',
- 'info',
- randomString,
- '--password',
- randomString,
- '--apple-id',
- randomString,
- '--team-id',
- randomString,
- ],
- stdout: 'status: Accepted',
- ),
- ]);
- codesignVisitor = cs.FileCodesignVisitor(
- codesignCertName: randomString,
- inputZipPath: inputZipPath,
- outputZipPath: outputZipPath,
- fileSystem: fileSystem,
- appSpecificPasswordFilePath: appSpecificPasswordFilePath,
- codesignAppstoreIDFilePath: codesignAppstoreIDFilePath,
- codesignTeamIDFilePath: codesignTeamIDFilePath,
- processManager: processManager,
- rootDirectory: rootDirectory,
- notarizationTimerDuration: const Duration(seconds: 0),
- dryrun: false,
- );
- codesignVisitor.appSpecificPassword = randomString;
- codesignVisitor.codesignAppstoreId = randomString;
- codesignVisitor.codesignTeamId = randomString;
- codesignVisitor.directoriesVisited.clear();
- await codesignVisitor.validateAll();
- final Set<String> messages = records
- .where((LogRecord record) => record.level == Level.INFO)
- .map((LogRecord record) => record.message)
- .toSet();
- expect(
- messages,
- contains('Codesign completed. Codesigned zip is located at ${codesignVisitor.outputZipPath}.'
- 'If you have uploaded the artifacts back to google cloud storage, please delete'
- ' the folder ${codesignVisitor.outputZipPath} and ${codesignVisitor.inputZipPath}.'),
- );
- expect(
- messages,
- isNot(
- contains('code signing dry run has completed, this is a quick sanity check without'
- 'going through the notary service. To run the full codesign process, use --no-dryrun flag.'),
- ),
- );
- });
- });
-}
diff --git a/cipd_packages/codesign/test/src/fake_process_manager.dart b/cipd_packages/codesign/test/src/fake_process_manager.dart
deleted file mode 100644
index 9aca69f..0000000
--- a/cipd_packages/codesign/test/src/fake_process_manager.dart
+++ /dev/null
@@ -1,446 +0,0 @@
-// Copyright 2014 The Flutter 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:async';
-import 'dart:convert';
-import 'dart:io' as io show ProcessSignal, Process, ProcessStartMode, ProcessResult, systemEncoding;
-
-import 'package:file/file.dart';
-import 'package:meta/meta.dart';
-import 'package:process/process.dart';
-import 'package:test/test.dart';
-
-export 'package:process/process.dart' show ProcessManager;
-
-typedef VoidCallback = void Function();
-
-/// A command for [FakeProcessManager].
-@immutable
-class FakeCommand {
- const FakeCommand({
- required this.command,
- this.workingDirectory,
- this.environment,
- this.encoding,
- this.duration = Duration.zero,
- this.onRun,
- this.exitCode = 0,
- this.stdout = '',
- this.stderr = '',
- this.completer,
- this.stdin,
- this.exception,
- this.outputFollowsExit = false,
- });
-
- /// The exact commands that must be matched for this [FakeCommand] to be
- /// considered correct.
- final List<String> command;
-
- /// The exact working directory that must be matched for this [FakeCommand] to
- /// be considered correct.
- ///
- /// If this is null, the working directory is ignored.
- final String? workingDirectory;
-
- /// The environment that must be matched for this [FakeCommand] to be considered correct.
- ///
- /// If this is null, then the environment is ignored.
- ///
- /// Otherwise, each key in this environment must be present and must have a
- /// value that matches the one given here for the [FakeCommand] to match.
- final Map<String, String>? environment;
-
- /// The stdout and stderr encoding that must be matched for this [FakeCommand]
- /// to be considered correct.
- ///
- /// If this is null, then the encodings are ignored.
- final Encoding? encoding;
-
- /// The time to allow to elapse before returning the [exitCode], if this command
- /// is "executed".
- ///
- /// If you set this to a non-zero time, you should use a [FakeAsync] zone,
- /// otherwise the test will be artificially slow.
- final Duration duration;
-
- /// A callback that is run after [duration] expires but before the [exitCode]
- /// (and output) are passed back.
- final VoidCallback? onRun;
-
- /// The process' exit code.
- ///
- /// To simulate a never-ending process, set [duration] to a value greater than
- /// 15 minutes (the timeout for our tests).
- ///
- /// To simulate a crash, subtract the crash signal number from 256. For example,
- /// SIGPIPE (-13) is 243.
- final int exitCode;
-
- /// The output to simulate on stdout. This will be encoded as UTF-8 and
- /// returned in one go.
- final String stdout;
-
- /// The output to simulate on stderr. This will be encoded as UTF-8 and
- /// returned in one go.
- final String stderr;
-
- /// If provided, allows the command completion to be blocked until the future
- /// resolves.
- final Completer<void>? completer;
-
- /// An optional stdin sink that will be exposed through the resulting
- /// [FakeProcess].
- final IOSink? stdin;
-
- /// If provided, this exception will be thrown when the fake command is run.
- final Object? exception;
-
- /// Indicates that output will only be emitted after the `exitCode` [Future]
- /// on [io.Process] completes.
- final bool outputFollowsExit;
-
- void _matches(
- List<String> command,
- String? workingDirectory,
- Map<String, String>? environment,
- Encoding? encoding,
- ) {
- expect(command, equals(this.command));
- if (this.workingDirectory != null) {
- expect(this.workingDirectory, workingDirectory);
- }
- if (this.environment != null) {
- expect(this.environment, environment);
- }
- if (this.encoding != null) {
- expect(this.encoding, encoding);
- }
- }
-}
-
-class _FakeProcess implements io.Process {
- _FakeProcess(
- this._exitCode,
- Duration duration,
- this.pid,
- this._stderr,
- IOSink? stdin,
- this._stdout,
- this._completer,
- bool outputFollowsExit,
- ) : exitCode = Future<void>.delayed(duration).then((void value) {
- if (_completer != null) {
- return _completer.future.then((void _) => _exitCode);
- }
- return _exitCode;
- }),
- stdin = stdin ?? IOSink(StreamController<List<int>>().sink) {
- if (_stderr == '') {
- stderr = const Stream<List<int>>.empty();
- } else if (outputFollowsExit) {
- stderr = Stream<List<int>>.fromFuture(
- exitCode.then((_) {
- return Future<List<int>>(() => utf8.encode(_stderr));
- }),
- );
- } else {
- stderr = Stream<List<int>>.value(utf8.encode(_stderr));
- }
-
- if (_stdout == '') {
- stdout = const Stream<List<int>>.empty();
- } else if (outputFollowsExit) {
- stdout = Stream<List<int>>.fromFuture(
- exitCode.then((_) {
- return Future<List<int>>(() => utf8.encode(_stdout));
- }),
- );
- } else {
- stdout = Stream<List<int>>.value(utf8.encode(_stdout));
- }
- }
-
- final int _exitCode;
- final Completer<void>? _completer;
-
- @override
- final Future<int> exitCode;
-
- @override
- final int pid;
-
- final String _stderr;
-
- @override
- late final Stream<List<int>> stderr;
-
- @override
- final IOSink stdin;
-
- @override
- late final Stream<List<int>> stdout;
-
- final String _stdout;
-
- @override
- bool kill([io.ProcessSignal signal = io.ProcessSignal.sigterm]) {
- // Killing a fake process has no effect.
- return false;
- }
-}
-
-abstract class FakeProcessManager implements ProcessManager {
- /// A fake [ProcessManager] which responds to all commands as if they had run
- /// instantaneously with an exit code of 0 and no output.
- factory FakeProcessManager.any() = _FakeAnyProcessManager;
-
- /// A fake [ProcessManager] which responds to particular commands with
- /// particular results.
- ///
- /// On creation, pass in a list of [FakeCommand] objects. When the
- /// [ProcessManager] methods such as [start] are invoked, the next
- /// [FakeCommand] must match (otherwise the test fails); its settings are used
- /// to simulate the result of running that command.
- ///
- /// If no command is found, then one is implied which immediately returns exit
- /// code 0 with no output.
- ///
- /// There is no logic to ensure that all the listed commands are run. Use
- /// [FakeCommand.onRun] to set a flag, or specify a sentinel command as your
- /// last command and verify its execution is successful, to ensure that all
- /// the specified commands are actually called.
- factory FakeProcessManager.list(List<FakeCommand> commands) = _SequenceProcessManager;
- factory FakeProcessManager.empty() => _SequenceProcessManager(<FakeCommand>[]);
-
- FakeProcessManager._();
-
- /// Adds a new [FakeCommand] to the current process manager.
- ///
- /// This can be used to configure test expectations after the [ProcessManager] has been
- /// provided to another interface.
- ///
- /// This is a no-op on [FakeProcessManager.any].
- void addCommand(FakeCommand command);
-
- /// Add multiple [FakeCommand] to the current process manager.
- void addCommands(Iterable<FakeCommand> commands) {
- commands.forEach(addCommand);
- }
-
- final Map<int, _FakeProcess> _fakeRunningProcesses = <int, _FakeProcess>{};
-
- /// Whether this fake has more [FakeCommand]s that are expected to run.
- ///
- /// This is always `true` for [FakeProcessManager.any].
- bool get hasRemainingExpectations;
-
- /// The expected [FakeCommand]s that have not yet run.
- List<FakeCommand> get _remainingExpectations;
-
- @protected
- FakeCommand findCommand(
- List<String> command,
- String? workingDirectory,
- Map<String, String>? environment,
- Encoding? encoding,
- );
-
- int _pid = 9999;
-
- _FakeProcess _runCommand(
- List<String> command,
- String? workingDirectory,
- Map<String, String>? environment,
- Encoding? encoding,
- ) {
- _pid += 1;
- final FakeCommand fakeCommand = findCommand(command, workingDirectory, environment, encoding);
- if (fakeCommand.exception != null) {
- assert(fakeCommand.exception is Exception || fakeCommand.exception is Error);
- throw fakeCommand.exception!; // ignore: only_throw_errors
- }
- if (fakeCommand.onRun != null) {
- fakeCommand.onRun!();
- }
- return _FakeProcess(
- fakeCommand.exitCode,
- fakeCommand.duration,
- _pid,
- fakeCommand.stderr,
- fakeCommand.stdin,
- fakeCommand.stdout,
- fakeCommand.completer,
- fakeCommand.outputFollowsExit,
- );
- }
-
- @override
- Future<io.Process> start(
- List<dynamic> command, {
- String? workingDirectory,
- Map<String, String>? environment,
- bool includeParentEnvironment = true, // ignored
- bool runInShell = false, // ignored
- io.ProcessStartMode mode = io.ProcessStartMode.normal, // ignored
- }) {
- final _FakeProcess process = _runCommand(command.cast<String>(), workingDirectory, environment, io.systemEncoding);
- if (process._completer != null) {
- _fakeRunningProcesses[process.pid] = process;
- process.exitCode.whenComplete(() {
- _fakeRunningProcesses.remove(process.pid);
- });
- }
- return Future<io.Process>.value(process);
- }
-
- @override
- Future<io.ProcessResult> run(
- List<dynamic> command, {
- String? workingDirectory,
- Map<String, String>? environment,
- bool includeParentEnvironment = true, // ignored
- bool runInShell = false, // ignored
- Encoding? stdoutEncoding = io.systemEncoding,
- Encoding? stderrEncoding = io.systemEncoding,
- }) async {
- final _FakeProcess process = _runCommand(command.cast<String>(), workingDirectory, environment, stdoutEncoding);
- await process.exitCode;
- return io.ProcessResult(
- process.pid,
- process._exitCode,
- stdoutEncoding == null ? process.stdout : await stdoutEncoding.decodeStream(process.stdout),
- stderrEncoding == null ? process.stderr : await stderrEncoding.decodeStream(process.stderr),
- );
- }
-
- @override
- io.ProcessResult runSync(
- List<dynamic> command, {
- String? workingDirectory,
- Map<String, String>? environment,
- bool includeParentEnvironment = true, // ignored
- bool runInShell = false, // ignored
- Encoding? stdoutEncoding = io.systemEncoding, // actual encoder is ignored
- Encoding? stderrEncoding = io.systemEncoding, // actual encoder is ignored
- }) {
- final _FakeProcess process = _runCommand(command.cast<String>(), workingDirectory, environment, stdoutEncoding);
- return io.ProcessResult(
- process.pid,
- process._exitCode,
- stdoutEncoding == null ? utf8.encode(process._stdout) : process._stdout,
- stderrEncoding == null ? utf8.encode(process._stderr) : process._stderr,
- );
- }
-
- /// Returns false if executable in [excludedExecutables].
- @override
- bool canRun(dynamic executable, {String? workingDirectory}) => !excludedExecutables.contains(executable);
-
- Set<String> excludedExecutables = <String>{};
-
- @override
- bool killPid(int pid, [io.ProcessSignal signal = io.ProcessSignal.sigterm]) {
- // Killing a fake process has no effect unless it has an attached completer.
- final _FakeProcess? fakeProcess = _fakeRunningProcesses[pid];
- if (fakeProcess == null) {
- return false;
- }
- if (fakeProcess._completer != null) {
- fakeProcess._completer!.complete();
- }
- return true;
- }
-}
-
-class _FakeAnyProcessManager extends FakeProcessManager {
- _FakeAnyProcessManager() : super._();
-
- @override
- FakeCommand findCommand(
- List<String> command,
- String? workingDirectory,
- Map<String, String>? environment,
- Encoding? encoding,
- ) {
- return FakeCommand(
- command: command,
- workingDirectory: workingDirectory,
- environment: environment,
- encoding: encoding,
- );
- }
-
- @override
- void addCommand(FakeCommand command) {}
-
- @override
- bool get hasRemainingExpectations => true;
-
- @override
- List<FakeCommand> get _remainingExpectations => <FakeCommand>[];
-}
-
-class _SequenceProcessManager extends FakeProcessManager {
- _SequenceProcessManager(this._commands) : super._();
-
- final List<FakeCommand> _commands;
-
- @override
- FakeCommand findCommand(
- List<String> command,
- String? workingDirectory,
- Map<String, String>? environment,
- Encoding? encoding,
- ) {
- expect(
- _commands,
- isNotEmpty,
- reason: 'ProcessManager was told to execute $command (in $workingDirectory) '
- 'but the FakeProcessManager.list expected no more processes.',
- );
- _commands.first._matches(command, workingDirectory, environment, encoding);
- return _commands.removeAt(0);
- }
-
- @override
- void addCommand(FakeCommand command) {
- _commands.add(command);
- }
-
- @override
- bool get hasRemainingExpectations => _commands.isNotEmpty;
-
- @override
- List<FakeCommand> get _remainingExpectations => _commands;
-}
-
-/// Matcher that successfully matches against a [FakeProcessManager] with
-/// no remaining expectations ([item.hasRemainingExpectations] returns false).
-const Matcher hasNoRemainingExpectations = _HasNoRemainingExpectations();
-
-class _HasNoRemainingExpectations extends Matcher {
- const _HasNoRemainingExpectations();
-
- @override
- bool matches(dynamic item, Map<dynamic, dynamic> matchState) =>
- item is FakeProcessManager && !item.hasRemainingExpectations;
-
- @override
- Description describe(Description description) =>
- description.add('a fake process manager with no remaining expectations');
-
- @override
- Description describeMismatch(
- dynamic item,
- Description description,
- Map<dynamic, dynamic> matchState,
- bool verbose,
- ) {
- final FakeProcessManager fakeProcessManager = item as FakeProcessManager;
- return description.add(
- 'has remaining expectations:\n${fakeProcessManager._remainingExpectations.map((FakeCommand command) => command.command).join('\n')}',
- );
- }
-}
diff --git a/cipd_packages/codesign/test/verify_test.dart b/cipd_packages/codesign/test/verify_test.dart
deleted file mode 100644
index 49d9d22..0000000
--- a/cipd_packages/codesign/test/verify_test.dart
+++ /dev/null
@@ -1,162 +0,0 @@
-// Copyright 2019 The Flutter 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 'package:codesign/verify.dart';
-import 'package:file/file.dart';
-import 'package:file/memory.dart';
-import 'package:logging/logging.dart';
-import 'package:test/test.dart';
-
-import 'src/fake_process_manager.dart';
-
-void main() {
- const String binaryPath = '/path/to/binary';
-
- late FakeProcessManager processManager;
- late FileSystem fs;
- late Logger logger;
- late VerificationService service;
- late List<LogRecord> logs;
-
- setUp(() {
- fs = MemoryFileSystem.test();
- fs.file(binaryPath).createSync(recursive: true);
- processManager = FakeProcessManager.empty();
- logs = <LogRecord>[];
- logger = Logger.detached('test');
- logger.onRecord.listen((LogRecord record) => logs.add(record));
- service = VerificationService(
- binaryPath: binaryPath,
- fs: fs,
- logger: logger,
- pm: processManager,
- );
- });
-
- test('parses codesign output and can present', () async {
- processManager.addCommands(const <FakeCommand>[
- FakeCommand(
- command: <String>['codesign', '--display', '-vv', binaryPath],
- stderr: '''
-Executable=$binaryPath
-Identifier=mybinary
-Format=Mach-O thin (x86_64)
-CodeDirectory v=20500 size=38000 flags=0x10000(runtime) hashes=1177+7 location=embedded
-Signature size=8979
-Authority=Developer ID Application: Dev Shop ABC (ABCC0VV123)
-Authority=Developer ID Certification Authority
-Authority=Apple Root CA
-Timestamp=Jan 9, 2023 at 9:39:07 AM
-Info.plist=not bound
-TeamIdentifier=ABCC0VV123
-Runtime Version=13.1.0
-Sealed Resources=none
-Internal requirements count=1 size=164
-''',
- ),
- FakeCommand(
- command: <String>[
- 'codesign',
- '--verify',
- '-v',
- '-R=notarized',
- '--check-notarization',
- binaryPath,
- ],
- stderr: '''
-$binaryPath: valid on disk
-$binaryPath: satisfies its Designated Requirement
-$binaryPath: explicit requirement satisfied
-''',
- ),
- ]);
- final VerificationResult result = await service.run();
- expect(processManager, hasNoRemainingExpectations);
- expect(result, VerificationResult.codesignedAndNotarized);
- expect(
- service.present(),
- '''
-Authority: Developer ID Application: Dev Shop ABC (ABCC0VV123)
-Time stamp: Jan 9, 2023 at 9:39:07 AM
-Format: Mach-O thin (x86_64)
-Signature size: 8979
-Notarization: true
-''',
- );
- });
-
- test('detects codesigned but not notarized binary', () async {
- processManager.addCommands(const <FakeCommand>[
- FakeCommand(
- command: <String>['codesign', '--display', '-vv', binaryPath],
- stderr: '''
-Executable=$binaryPath
-Identifier=mybinary
-Format=Mach-O thin (x86_64)
-CodeDirectory v=20500 size=38000 flags=0x10000(runtime) hashes=1177+7 location=embedded
-Signature size=8979
-Authority=Developer ID Application: Dev Shop ABC (ABCC0VV123)
-Authority=Developer ID Certification Authority
-Authority=Apple Root CA
-Timestamp=Jan 9, 2023 at 9:39:07 AM
-Info.plist=not bound
-TeamIdentifier=ABCC0VV123
-Runtime Version=13.1.0
-Sealed Resources=none
-Internal requirements count=1 size=164
-''',
- ),
- FakeCommand(
- command: <String>[
- 'codesign',
- '--verify',
- '-v',
- '-R=notarized',
- '--check-notarization',
- binaryPath,
- ],
- stderr: '''
-$binaryPath: valid on disk
-$binaryPath: satisfies its Designated Requirement
-test-requirement: code failed to satisfy specified code requirement(s)
-''',
- exitCode: 3,
- ),
- ]);
- final VerificationResult result = await service.run();
- expect(processManager, hasNoRemainingExpectations);
- expect(result, VerificationResult.codesignedOnly);
- expect(
- service.present(),
- '''
-Authority: Developer ID Application: Dev Shop ABC (ABCC0VV123)
-Time stamp: Jan 9, 2023 at 9:39:07 AM
-Format: Mach-O thin (x86_64)
-Signature size: 8979
-Notarization: false
-''',
- );
- });
-
- test('detects unsigned binary', () async {
- processManager.addCommands(const <FakeCommand>[
- FakeCommand(
- command: <String>['codesign', '--display', '-vv', binaryPath],
- stderr: '$binaryPath: code object is not signed at all',
- exitCode: 1,
- ),
- ]);
- final VerificationResult result = await service.run();
- expect(processManager, hasNoRemainingExpectations);
- expect(result, VerificationResult.unsigned);
- expect(
- logs.first,
- isA<LogRecord>().having(
- (LogRecord record) => record.message,
- 'message',
- contains('File $binaryPath is not codesigned.'),
- ),
- );
- });
-}
diff --git a/cipd_packages/codesign/tool/build.sh b/cipd_packages/codesign/tool/build.sh
deleted file mode 100755
index fe8f010..0000000
--- a/cipd_packages/codesign/tool/build.sh
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/usr/bin/env bash
-# Copyright 2019 The Flutter Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-# Fetches corresponding dart sdk from CIPD for different platforms, builds
-# an executable binary of codesign to `build` folder.
-#
-# This build script will be triggered on Mac code signing machines.
-
-set -e
-
-command -v cipd > /dev/null || {
- echo "Please install CIPD (available from depot_tools) and add to path first.";
- exit -1;
-}
-
-DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd)"
-OS="`uname`"
-
-cipd ensure -ensure-file $DIR/ensure_file -root $DIR
-
-pushd $DIR/..
-
-if [[ -d "build" ]]; then
- echo "Please remove the build directory before proceeding"
- exit -1
-fi
-
-mkdir -p build
-tool/bin/dart pub get
-tool/bin/dart compile exe bin/codesign.dart -o build/codesign
-
-cp -f LICENSE build/
-
-popd
diff --git a/cipd_packages/codesign/tool/ensure_file b/cipd_packages/codesign/tool/ensure_file
deleted file mode 100644
index a6985f8..0000000
--- a/cipd_packages/codesign/tool/ensure_file
+++ /dev/null
@@ -1,3 +0,0 @@
-$ServiceURL https://chrome-infra-packages.appspot.com/
-
-dart/dart-sdk/${os}-${arch} beta
diff --git a/cipd_packages/device_doctor/.gitignore b/cipd_packages/device_doctor/.gitignore
deleted file mode 100644
index c206c45..0000000
--- a/cipd_packages/device_doctor/.gitignore
+++ /dev/null
@@ -1,11 +0,0 @@
-# CIPD and Dart-SDK binaries
-.cipd/
-dart-sdk/
-
-# Build outputs
-.dart_tool/
-build/
-.ssh/
-
-# Dart
-.packages
diff --git a/cipd_packages/device_doctor/LICENSE b/cipd_packages/device_doctor/LICENSE
deleted file mode 100644
index d5384ca..0000000
--- a/cipd_packages/device_doctor/LICENSE
+++ /dev/null
@@ -1,27 +0,0 @@
-Copyright 2016 The Flutter Authors. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
- * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/cipd_packages/device_doctor/README.md b/cipd_packages/device_doctor/README.md
deleted file mode 100644
index 5311ecd..0000000
--- a/cipd_packages/device_doctor/README.md
+++ /dev/null
@@ -1,89 +0,0 @@
-# device_doctor
-
-This utility tool is used by LUCI infrastructure to manage device health
-for Flutter LUCI swarming bots.
-
-It offers support for different host platforms: linux, mac and windows, and
-different devices: android and iOS.
-
-## Dependencies
-
-Different devices require different tools to be in the `path` beforehand.
-
-### android
-* `adb`
-
-### iOS
-* `idevice_id`
-* `idevicediagnostics`
-* `xcrun`
-
-## Building
-
-This tool is meant to be published as an
-[AOT compiled binary](https://chrome-infra-packages.appspot.com/p/flutter/device_doctor)
-distributed via CIPD.
-
-Build the tool for different host platforms on corresponding machines. It will
-automatically download a suitable version of Dart to build the binary.
-
-To create the CIPD package, make sure that the `build/` folder does not exist.
-
-### Auto build
-
-Every new commit will trigger pre-submit builders to auto build a new version
-for different platforms without any tag/ref.
-
-When a new commit is submitted, post-submit builders will trigger a new version
-with a tag of `commit_sha`, and a ref of `latest`.
-
-### Manual build
-
-Running `tool/build.sh` or `tool/build.bat` will build an executable binary in
-the `build` folder. Then push to cipd by running
-
-```bash
-cipd create -in build \
- -name flutter/device_doctor/<os>-amd64 \
- -ref <ref> \
- -tag sha_timestamp:<revision>_<timestamp>
-```
-
-* os: `linux`, `mac`, or `windows`.
-* ref: `release` or `staging`
-
-## How to use
-
-`device_doctor` is the executable binary, and can be called
-
-```bash
-/path/to/device_doctor --action <healthcheck|recovery|properties> --device-os <android|ios>
-```
-
-Use `/path/to/device_doctor --help` to learn more.
-
-**Note**: this tool is assuming one connected device on each host, but can be easily extended
-to support multiple devices.
-
-## Releasing device_doctor
-
-The release process of the tool is controlled by setting `release` and `staging` refs.
-
-To promote a given version to staging you can run the following command:
-
-```
-cipd set-ref flutter/device_doctor/<os>-<arch> -ref staging -version <packageid>
-```
-
-Example for mac and amd64:
-
-```
-cipd set-ref flutter/device_doctor/mac-amd64 -ref staging -version IQgKjNstWbFhUuMVp898zZoPKRd66KLRKuiY88XYQXAC
-```
-
-After extensive validation on staging you can promote the package to release using:
-
-
-```
-cipd set-ref flutter/device_doctor/<os>-<arch> -ref release -version <packageid>
-```
diff --git a/cipd_packages/device_doctor/bin/ios_debug_symbol_doctor.dart b/cipd_packages/device_doctor/bin/ios_debug_symbol_doctor.dart
deleted file mode 100644
index aed5f76..0000000
--- a/cipd_packages/device_doctor/bin/ios_debug_symbol_doctor.dart
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2020 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/// This is not a part of the main Device Doctor because it depends on Xcode
-/// being installed and set up in the environment, while the main iOS Device
-/// Doctor workflow runs before Xcode is provisioned.
-
-import 'dart:io';
-
-import 'package:args/command_runner.dart';
-import 'package:device_doctor/device_doctor.dart';
-import 'package:logging/logging.dart';
-
-const String helpFlag = 'help';
-
-Future<void> main(List<String> args) async {
- // Write logs to stdout
- Logger.root.onRecord.listen((LogRecord record) {
- stdout.writeln(record.toString());
- });
-
- final CommandRunner<bool> runner = CommandRunner<bool>(
- 'ios-debug-symbol-doctor',
- 'Tool for diagnosing and recovering from iOS debug symbols not synched with the host by Xcode',
- )
- ..addCommand(DiagnoseCommand())
- ..addCommand(RecoverCommand());
-
- final bool? success = await runner.run(args);
- exit(success == true ? 0 : 1);
-}
diff --git a/cipd_packages/device_doctor/bin/main.dart b/cipd_packages/device_doctor/bin/main.dart
deleted file mode 100644
index a8beff1..0000000
--- a/cipd_packages/device_doctor/bin/main.dart
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2020 The Flutter 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:args/args.dart';
-
-import 'package:device_doctor/device_doctor.dart';
-
-const String actionFlag = 'action';
-const String deviceOSFlag = 'device-os';
-const String helpFlag = 'help';
-const String outputFlag = 'output';
-const List<String> supportedOptions = <String>['healthcheck', 'prepare', 'recovery', 'properties'];
-const List<String> supportedDeviceOS = <String>['ios', 'android'];
-const String defaultOutputPath = '.output';
-
-/// These values will be initialized in `_checkArgs` function,
-/// and used in `main` function.
-String? _action;
-String? _deviceOS;
-File? _output;
-
-/// Manage `healthcheck`, `prepare, `recovery`, and `properties` for devices.
-///
-/// For `healthcheck`, if no device is found or any health check fails an stderr will be logged,
-/// and an exception will be thrown.
-///
-/// For `recovery`, it will do cleanup, reboot, etc. to try bringing device back to a working state.
-///
-/// For `prepare`, it will prepare the device before running tasks, like kill running processes, etc.
-///
-/// For `properties`, it will return device properties/dimensions, like manufacture, base_buildid, etc.
-///
-/// Usage:
-/// dart main.dart --action <healthcheck|recovery> --deviceOS <android|ios>
-Future<void> main(List<String> args) async {
- final ArgParser parser = ArgParser();
- parser
- ..addFlag(
- '$helpFlag',
- help: 'Prints usage info.',
- abbr: 'h',
- callback: (bool value) {
- if (value) {
- stdout.write('${parser.usage}\n');
- exit(1);
- }
- },
- )
- ..addOption(
- '$actionFlag',
- help: 'Supported actions.',
- allowed: supportedOptions,
- allowedHelp: {
- 'healthcheck': 'Check device health status.',
- 'recovery': 'Clean up and reboot device.',
- 'properties': 'Return device properties/dimensions.',
- },
- )
- ..addOption('$outputFlag', help: 'Path to the output file')
- ..addOption(
- '$deviceOSFlag',
- help: 'Supported device OS.',
- allowed: supportedDeviceOS,
- allowedHelp: {'android': 'Available for linux, mac, and windows.', 'ios': 'Available for mac.'},
- );
-
- final ArgResults argResults = parser.parse(args);
- _action = argResults[actionFlag];
- _deviceOS = argResults[deviceOSFlag];
- _output = File(argResults[outputFlag] ?? defaultOutputPath);
-
- final DeviceDiscovery deviceDiscovery = DeviceDiscovery(_deviceOS, _output);
-
- switch (_action) {
- case 'healthcheck':
- await deviceDiscovery.checkDevices();
- break;
- case 'prepare':
- await deviceDiscovery.prepareDevices();
- break;
- case 'recovery':
- await deviceDiscovery.recoverDevices();
- break;
- case 'properties':
- await deviceDiscovery.deviceProperties();
- }
-}
diff --git a/cipd_packages/device_doctor/lib/device_doctor.dart b/cipd_packages/device_doctor/lib/device_doctor.dart
deleted file mode 100644
index 82aac9c..0000000
--- a/cipd_packages/device_doctor/lib/device_doctor.dart
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright 2020 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-export 'src/device.dart';
-export 'src/health.dart';
-export 'src/host_utils.dart';
-export 'src/ios_device.dart';
-export 'src/utils.dart';
-export 'src/ios_debug_symbol_doctor.dart';
diff --git a/cipd_packages/device_doctor/lib/src/android_device.dart b/cipd_packages/device_doctor/lib/src/android_device.dart
deleted file mode 100644
index 8d5062b..0000000
--- a/cipd_packages/device_doctor/lib/src/android_device.dart
+++ /dev/null
@@ -1,505 +0,0 @@
-// Copyright 2020 The Flutter 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:async';
-import 'dart:convert';
-import 'dart:io';
-
-import 'package:meta/meta.dart';
-import 'package:process/process.dart';
-import 'package:retry/retry.dart';
-
-import 'device.dart';
-import 'health.dart';
-import 'host_utils.dart';
-import 'mac.dart';
-import 'utils.dart';
-
-// The minimum battery level to run a task with a scale of 100%.
-const int _kBatteryMinLevel = 15;
-// The maximum battery temprature to run a task with a Celsius degree.
-const int _kBatteryMaxTemperatureInCelsius = 34;
-
-class AndroidDeviceDiscovery implements DeviceDiscovery {
- factory AndroidDeviceDiscovery(File? output) {
- return _instance ??= AndroidDeviceDiscovery._(output);
- }
-
- final File? _outputFilePath;
- AndroidDeviceDiscovery._(this._outputFilePath);
-
- @visibleForTesting
- AndroidDeviceDiscovery.testing(this._outputFilePath);
-
- // Parses information about a device. Example:
- //
- // 015d172c98400a03 device usb:340787200X product:nakasi model:Nexus_7 device:grouper
- static final RegExp _kDeviceRegex = RegExp(r'^(\S+)\s+(\S+)(.*)');
-
- static AndroidDeviceDiscovery? _instance;
-
- Future<String> _deviceListOutput(Duration timeout, {ProcessManager? processManager}) async {
- return eval('adb', <String>['devices', '-l'], canFail: false, processManager: processManager).timeout(timeout);
- }
-
- Future<List<String>> _deviceListOutputWithRetries(Duration retryDuration, {ProcessManager? processManager}) async {
- const Duration deviceOutputTimeout = Duration(seconds: 15);
- final RetryOptions r = RetryOptions(
- maxAttempts: 3,
- delayFactor: retryDuration,
- );
- return r.retry(
- () async {
- final String result = await _deviceListOutput(deviceOutputTimeout, processManager: processManager);
- return result.trim().split('\n');
- },
- retryIf: (Exception e) => e is TimeoutException,
- onRetry: (Exception e) => _killAdbServer(processManager: processManager),
- );
- }
-
- void _killAdbServer({ProcessManager? processManager}) async {
- if (Platform.isWindows) {
- await killAllRunningProcessesOnWindows('adb', processManager: processManager);
- } else {
- await eval('adb', <String>['kill-server'], canFail: false, processManager: processManager);
- }
- }
-
- @override
- Future<List<AndroidDevice>> discoverDevices({
- Duration retryDuration = const Duration(seconds: 10),
- ProcessManager? processManager,
- }) async {
- processManager ??= LocalProcessManager();
- final List<String> output = await _deviceListOutputWithRetries(retryDuration, processManager: processManager);
- final List<String> results = <String>[];
- for (String line in output) {
- // Skip lines like: * daemon started successfully *
- if (line.startsWith('* daemon ')) continue;
-
- if (line.startsWith('List of devices')) continue;
-
- if (_kDeviceRegex.hasMatch(line)) {
- final Match? match = _kDeviceRegex.firstMatch(line);
-
- final String? deviceID = match?[1];
- final String? deviceState = match?[2];
-
- if (!const ['unauthorized', 'offline'].contains(deviceState)) {
- results.add(deviceID!);
- }
- } else {
- throw 'Failed to parse device from adb output: $line';
- }
- }
- return results.map((String id) => AndroidDevice(deviceId: id)).toList();
- }
-
- @override
- Future<Map<String, List<HealthCheckResult>>> checkDevices({ProcessManager? processManager}) async {
- processManager ??= LocalProcessManager();
- final List<HealthCheckResult> defaultChecks = <HealthCheckResult>[];
- defaultChecks.add(await killAdbServerCheck(processManager: processManager));
- final Map<String, List<HealthCheckResult>> results = <String, List<HealthCheckResult>>{};
- for (AndroidDevice device in await discoverDevices(processManager: processManager)) {
- final List<HealthCheckResult> checks = defaultChecks;
- checks.add(HealthCheckResult.success(kDeviceAccessCheckKey));
- checks.add(await adbPowerServiceCheck(processManager: processManager));
- checks.add(await developerModeCheck(processManager: processManager));
- checks.add(await screenOnCheck(processManager: processManager));
- checks.add(await screenSaverCheck(processManager: processManager));
- checks.add(await screenRotationCheck(processManager: processManager));
- checks.add(await batteryLevelCheck(processManager: processManager));
- checks.add(await batteryTemperatureCheck(processManager: processManager));
- if (Platform.isMacOS) {
- checks.add(await userAutoLoginCheck(processManager: processManager));
- }
- results['android-device-${device.deviceId}'] = checks;
- }
- final Map<String, Map<String, dynamic>> healthCheckMap = await healthcheck(results);
- writeToFile(json.encode(healthCheckMap), _outputFilePath!);
- return results;
- }
-
- /// Checks and returns the device properties, like manufacturer, base_buildid, etc.
- ///
- /// It supports multiple devices, but here we are assuming only one device is attached.
- @override
- Future<Map<String, String>> deviceProperties({ProcessManager? processManager}) async {
- final List<AndroidDevice> devices = await discoverDevices(processManager: processManager);
- Map<String, String> properties = <String, String>{};
- if (devices.isEmpty) {
- writeToFile(json.encode(properties), _outputFilePath!);
- stdout.writeln('No devices available.');
- return properties;
- }
- properties = await getDeviceProperties(devices[0], processManager: processManager);
- final String propertiesJson = json.encode(properties);
-
- writeToFile(propertiesJson, _outputFilePath!);
- stdout.writeln('Properties for deviceID ${devices[0].deviceId}: $propertiesJson');
- return properties;
- }
-
- /// Gets android device properties based on swarming bot configuration.
- ///
- /// Refer function `get_dimensions` from
- /// https://source.chromium.org/chromium/infra/infra/+/master:luci/appengine/swarming/swarming_bot/api/platforms/android.py
- Future<Map<String, String>> getDeviceProperties(AndroidDevice device, {ProcessManager? processManager}) async {
- processManager ??= LocalProcessManager();
- final Map<String, String> deviceProperties = <String, String>{};
- final Map<String, String> propertyMap = <String, String>{};
- LineSplitter.split(
- await eval('adb', <String>['-s', device.deviceId!, 'shell', 'getprop'], processManager: processManager),
- ).forEach((String property) {
- final List<String> propertyList = property.replaceAll('[', '').replaceAll(']', '').split(': ');
-
- /// Deal with entries spanning only one line.
- ///
- /// This is to skip unused entries spanninning multiple lines.
- /// For example:
- /// [persist.sys.boot.reason.history]: [reboot,ota,1613677289
- /// reboot,userrequested,1613677269
- /// reboot,userrequested,1613508544]
- if (propertyList.length == 2) {
- propertyMap[propertyList[0].trim()] = propertyList[1].trim();
- }
- });
-
- deviceProperties['product_brand'] = propertyMap['ro.product.brand']!;
- deviceProperties['build_id'] = propertyMap['ro.build.id']!;
- deviceProperties['build_type'] = propertyMap['ro.build.type']!;
- deviceProperties['product_model'] = propertyMap['ro.product.model']!;
- deviceProperties['product_board'] = propertyMap['ro.product.board']!;
- return deviceProperties;
- }
-
- @override
- Future<void> recoverDevices() async {
- for (Device device in await discoverDevices()) {
- await device.recover();
- }
- }
-
- @visibleForTesting
- Future<HealthCheckResult> adbPowerServiceCheck({ProcessManager? processManager}) async {
- HealthCheckResult healthCheckResult;
- try {
- await eval('adb', <String>['shell', 'dumpsys', 'power'], processManager: processManager);
- healthCheckResult = HealthCheckResult.success(kAdbPowerServiceCheckKey);
- } on BuildFailedError catch (error) {
- healthCheckResult = HealthCheckResult.failure(kAdbPowerServiceCheckKey, error.toString());
- }
- return healthCheckResult;
- }
-
- @visibleForTesting
-
- /// The health check for Android device screen on.
- ///
- /// An Android device screen is on when both `mHoldingWakeLockSuspendBlocker` and
- /// `mHoldingDisplaySuspendBlocker` are true.
- Future<HealthCheckResult> screenOnCheck({ProcessManager? processManager}) async {
- HealthCheckResult healthCheckResult;
- try {
- final String result = await eval(
- 'adb',
- <String>['shell', 'dumpsys', 'power', '|', 'grep', 'mHoldingDisplaySuspendBlocker'],
- processManager: processManager,
- );
- if (result.trim() == 'mHoldingDisplaySuspendBlocker=true') {
- healthCheckResult = HealthCheckResult.success(kScreenOnCheckKey);
- } else {
- healthCheckResult = HealthCheckResult.failure(kScreenOnCheckKey, 'screen is off');
- }
- } on BuildFailedError catch (error) {
- healthCheckResult = HealthCheckResult.failure(kScreenOnCheckKey, error.toString());
- }
- return healthCheckResult;
- }
-
- @visibleForTesting
-
- /// The health check for Android device adb kill server.
- ///
- /// Kill adb server before running any health check to avoid device quarantine:
- /// https://github.com/flutter/flutter/issues/93075.
- Future<HealthCheckResult> killAdbServerCheck({ProcessManager? processManager}) async {
- HealthCheckResult healthCheckResult;
- try {
- await eval('adb', <String>['kill-server'], processManager: processManager);
- healthCheckResult = HealthCheckResult.success(kKillAdbServerCheckKey);
- } on BuildFailedError catch (error) {
- healthCheckResult = HealthCheckResult.failure(kKillAdbServerCheckKey, error.toString());
- }
- return healthCheckResult;
- }
-
- @visibleForTesting
-
- /// The health check for Android device developer mode.
- ///
- /// Developer mode `on` is expected for a healthy Android device.
- Future<HealthCheckResult> developerModeCheck({ProcessManager? processManager}) async {
- HealthCheckResult healthCheckResult;
- try {
- final String result = await eval(
- 'adb',
- <String>['shell', 'settings', 'get', 'global', 'development_settings_enabled'],
- processManager: processManager,
- );
- // The output of `development_settings_enabled` is `1` when developer mode is on.
- if (result == '1') {
- healthCheckResult = HealthCheckResult.success(kDeveloperModeCheckKey);
- } else {
- healthCheckResult = HealthCheckResult.failure(kDeveloperModeCheckKey, 'developer mode is off');
- }
- } on BuildFailedError catch (error) {
- healthCheckResult = HealthCheckResult.failure(kDeveloperModeCheckKey, error.toString());
- }
- return healthCheckResult;
- }
-
- /// The health check to validate screen rotation is off.
- ///
- /// Screen rotation is expected disabled for a healthy Android device.
- Future<HealthCheckResult> screenRotationCheck({ProcessManager? processManager}) async {
- HealthCheckResult healthCheckResult;
- try {
- final String result = await eval(
- 'adb',
- <String>['shell', 'settings', 'get', 'system', 'accelerometer_rotation'],
- processManager: processManager,
- );
- // The output of `screensaver_enabled` is `0` when screensaver mode is off.
- if (result == '0') {
- healthCheckResult = HealthCheckResult.success(kScreenRotationCheckKey);
- } else {
- healthCheckResult = HealthCheckResult.failure(kScreenRotationCheckKey, 'Screen rotation is enabled');
- }
- } on BuildFailedError catch (error) {
- healthCheckResult = HealthCheckResult.failure(kScreenRotationCheckKey, error.toString());
- }
- return healthCheckResult;
- }
-
- /// The health check to validate screensaver is off.
- ///
- /// Screensaver`off` is expected for a healthy Android device.
- Future<HealthCheckResult> screenSaverCheck({ProcessManager? processManager}) async {
- HealthCheckResult healthCheckResult;
- try {
- final String result = await eval(
- 'adb',
- <String>['shell', 'settings', 'get', 'secure', 'screensaver_enabled'],
- processManager: processManager,
- );
- // The output of `screensaver_enabled` is `0` when screensaver mode is off.
- if (result == '0') {
- healthCheckResult = HealthCheckResult.success(kScreenSaverCheckKey);
- } else {
- healthCheckResult = HealthCheckResult.failure(kScreenSaverCheckKey, 'Screensaver is on');
- }
- } on BuildFailedError catch (error) {
- healthCheckResult = HealthCheckResult.failure(kScreenSaverCheckKey, error.toString());
- }
- return healthCheckResult;
- }
-
- /// The health check for battery level.
- Future<HealthCheckResult> batteryLevelCheck({ProcessManager? processManager}) async {
- HealthCheckResult healthCheckResult;
- try {
- // The battery level returns two rows. For example:
- // level: 100
- // mod level: -1
- final String levelResults = await eval(
- 'adb',
- <String>['shell', 'dumpsys', 'battery', '|', 'grep', 'level'],
- processManager: processManager,
- );
- final RegExp levelRegExp = RegExp('level: (?<level>.+)');
- final RegExpMatch? match = levelRegExp.firstMatch(levelResults);
- final int level = int.parse(match!.namedGroup('level')!);
- if (level < _kBatteryMinLevel) {
- healthCheckResult =
- HealthCheckResult.failure(kBatteryLevelCheckKey, 'Battery level ($level) is below $_kBatteryMinLevel');
- } else {
- healthCheckResult = HealthCheckResult.success(kBatteryLevelCheckKey);
- }
- } on BuildFailedError catch (error) {
- healthCheckResult = HealthCheckResult.failure(kScreenSaverCheckKey, error.toString());
- }
- return healthCheckResult;
- }
-
- /// The health check for battery temperature.
- Future<HealthCheckResult> batteryTemperatureCheck({ProcessManager? processManager}) async {
- HealthCheckResult healthCheckResult;
- try {
- // The battery temperature returns one row. For example:
- // temperature: 240
- // It means 24°C.
- final String tempResult = await eval(
- 'adb',
- <String>['shell', 'dumpsys', 'battery', '|', 'grep', 'temperature'],
- processManager: processManager,
- );
- final RegExp? tempRegExp = RegExp('temperature: (?<temperature>.+)');
- final RegExpMatch match = tempRegExp!.firstMatch(tempResult)!;
- final int temperature = int.parse(match.namedGroup('temperature')!);
- if (temperature > _kBatteryMaxTemperatureInCelsius * 10) {
- healthCheckResult = HealthCheckResult.failure(
- kBatteryTemperatureCheckKey,
- 'Battery temperature (${(temperature * 0.1).toInt()}°C) is over $_kBatteryMaxTemperatureInCelsius°C',
- );
- } else {
- healthCheckResult = HealthCheckResult.success(kBatteryTemperatureCheckKey);
- }
- } on BuildFailedError catch (error) {
- healthCheckResult = HealthCheckResult.failure(kBatteryTemperatureCheckKey, error.toString());
- }
- return healthCheckResult;
- }
-
- @override
- Future<void> prepareDevices() async {
- for (Device device in await discoverDevices()) {
- await device.prepare();
- }
- }
-}
-
-class AndroidDevice implements Device {
- AndroidDevice({@required this.deviceId});
-
- @override
- final String? deviceId;
-
- @override
- Future<void> recover() async {
- await cleanDevice();
- }
-
- @visibleForTesting
- Future<void> deletePackageCache() async {
- final ProcessResult result = Process.runSync('adb', <String>['shell', 'pm', 'list', 'packages']);
-
- if (result.exitCode != 0) {
- throw Exception(result.stderr as String);
- }
- final packages = <String>[];
-
- // Listen to the stdout and stderr streams
- LineSplitter().convert(result.stdout).forEach((data) {
- final packageMatch = RegExp(r'package:(.+)$').firstMatch(data);
- if (packageMatch != null) {
- final packageName = packageMatch.group(1);
- if (packageName != null) {
- packages.add(packageName);
- }
- }
- });
- for (final p in packages) {
- final r = Process.runSync('adb', <String>['shell', 'pm', 'clear', p]);
- if (r.exitCode != 0) {
- print('Clearing package $p resulted in a non-zero exit.');
- print('STDERR: ${r.stderr}');
- print('STDOUT: ${r.stdout}');
- } else {
- print('Uninstalling package $p : STDOUT(${r.stdout})');
- }
- }
- }
-
- @visibleForTesting
- Future<void> delete3Ppackages() async {
- final ProcessResult result = Process.runSync('adb', <String>['shell', 'pm', 'list', 'packages', '-3']);
- if (result.exitCode != 0) {
- throw Exception(result.stderr as String);
- }
- final packages = <String>[];
-
- // Listen to the stdout and stderr streams
- LineSplitter().convert(result.stdout).forEach((data) {
- final packageMatch = RegExp(r'package:(.+)$').firstMatch(data);
- if (packageMatch != null) {
- final packageName = packageMatch.group(1);
- if (packageName != null) {
- packages.add(packageName);
- }
- }
- });
- for (final p in packages) {
- final r = Process.runSync('adb', <String>['shell', 'pm', 'uninstall', p]);
- if (r.exitCode != 0) {
- print('Uninstalling package $p resulted in a non-zero exit.');
- print('STDERR: ${r.stderr}');
- print('STDOUT: ${r.stdout}');
- } else {
- print('Uninstalling package $p : STDOUT(${r.stdout})');
- }
- }
- }
-
- @visibleForTesting
- Future<bool> cleanDevice({ProcessManager? processManager}) async {
- processManager ??= LocalProcessManager();
- final int timeoutSecs = 60;
- print('Device recovery: deleting package caches...');
- await eval('adb', <String>['wait-for-device'], canFail: false, processManager: processManager)
- .timeout(Duration(seconds: timeoutSecs));
- await deletePackageCache();
- await eval('adb', <String>['wait-for-device'], canFail: false, processManager: processManager)
- .timeout(Duration(seconds: timeoutSecs));
- print('Device recovery: deleting 3P packages...');
- await delete3Ppackages();
- await eval('adb', <String>['wait-for-device'], canFail: false, processManager: processManager)
- .timeout(Duration(seconds: timeoutSecs));
- print('Device recovery: rebooting...');
- await eval('adb', <String>['reboot'], canFail: false, processManager: processManager);
- return true;
- }
-
- @override
- Future<void> prepare() async {
- await killProcesses();
- }
-
- /// Kill top running process if existing.
- @visibleForTesting
- Future<bool> killProcesses({ProcessManager? processManager}) async {
- processManager ??= LocalProcessManager();
- String result;
- result = await eval(
- 'adb',
- <String>['shell', 'dumpsys', 'activity', '|', 'grep', 'top-activity'],
- canFail: true,
- processManager: processManager,
- );
-
- // Skip uninstalling process when no device is available or no application exists.
- if (result == 'adb: no devices/emulators found' || result.isEmpty) {
- stdout.write('no process is running');
- return true;
- }
- final List<String> results = result.trim().split('\n');
- // Example result:
- //
- // Proc # 0: fore T/A/T trm: 0 4544:com.google.android.googlequicksearchbox/u0a66 (top-activity)
- final List<String> processes =
- results.map((result) => result.substring(result.lastIndexOf(':') + 1, result.lastIndexOf('/'))).toList();
- try {
- for (String process in processes) {
- await eval('adb', <String>['shell', 'am', 'force-stop', process], processManager: processManager);
- stdout.write('adb stop process: $process');
- }
- } on BuildFailedError catch (error) {
- stderr.write('uninstall applications fails: $error');
- return false;
- }
- return true;
- }
-}
diff --git a/cipd_packages/device_doctor/lib/src/device.dart b/cipd_packages/device_doctor/lib/src/device.dart
deleted file mode 100644
index 99ef4c4..0000000
--- a/cipd_packages/device_doctor/lib/src/device.dart
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2020 The Flutter 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:async';
-import 'dart:io';
-
-import 'package:process/process.dart';
-
-import 'android_device.dart';
-import 'health.dart';
-import 'ios_device.dart';
-
-/// Discovers available devices and chooses one to work with.
-abstract class DeviceDiscovery {
- factory DeviceDiscovery(String? deviceOs, File? output) {
- switch (deviceOs) {
- case 'ios':
- return IosDeviceDiscovery(output);
- case 'android':
- return AndroidDeviceDiscovery(output);
- default:
- throw StateError('Unsupported device operating system: $deviceOs');
- }
- }
-
- /// Lists all available devices' IDs.
- Future<List<Device>> discoverDevices({Duration retryDuration = const Duration(seconds: 10)});
-
- /// Checks the health of the available devices.
- Future<Map<String, List<HealthCheckResult>>> checkDevices({ProcessManager processManager});
-
- /// Checks and returns the device properties, like manufacturer, base_buildid, etc.
- ///
- /// Currently it supports only android devices, but can extend to iOS devices.
- Future<Map<String, String>> deviceProperties({ProcessManager processManager});
-
- /// Recovers the device.
- Future<void> recoverDevices();
-
- /// Prepares the device.
- Future<void> prepareDevices();
-}
-
-/// A proxy for one specific phone device.
-abstract class Device {
- /// A unique device identifier.
- String? get deviceId;
-
- /// Recovers the device back to a healthy state.
- Future<void> recover();
-
- /// Prepares the device before running tasks.
- Future<void> prepare();
-}
diff --git a/cipd_packages/device_doctor/lib/src/health.dart b/cipd_packages/device_doctor/lib/src/health.dart
deleted file mode 100644
index bb78ae3..0000000
--- a/cipd_packages/device_doctor/lib/src/health.dart
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright 2020 The Flutter 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:async';
-import 'dart:io';
-
-import 'package:path/path.dart' as path;
-import 'package:platform/platform.dart' as platform;
-import 'package:process/process.dart';
-
-import 'utils.dart';
-
-/// Closes system dialogs on iOS, e.g. the one about new system update.
-///
-/// The dialogs often cause test flakiness and performance regressions.
-Future<HealthCheckResult> closeIosDialog({
- ProcessManager pm = const LocalProcessManager(),
- String? deviceId,
- platform.Platform pl = const platform.LocalPlatform(),
- String infraDialog = 'infra-dialog',
-}) async {
- Directory dialogDir = dir(path.dirname(Platform.script.path), 'tool', infraDialog);
- if (!await dialogDir.exists()) {
- dialogDir = dir(Directory.current.path, 'tool', infraDialog);
- if (!await dialogDir.exists()) {
- fail('Unable to find infra-dialog at $dialogDir');
- }
- }
-
- // Runs the single XCUITest in infra-dialog.
- await inDirectory(dialogDir, () async {
- final List<String> command =
- 'xcrun xcodebuild -project infra-dialog.xcodeproj -scheme infra-dialog -destination -quiet id=$deviceId test'
- .split(' ');
- // By default the above command relies on automatic code signing, while on devicelab machines
- // it should utilize manual code signing as that is more stable. Below overwrites the code
- // signing config if one exists in the environment.
- if (pl.environment['FLUTTER_XCODE_CODE_SIGN_STYLE'] != null) {
- command.add("CODE_SIGN_STYLE=${pl.environment['FLUTTER_XCODE_CODE_SIGN_STYLE']}");
- command.add("DEVELOPMENT_TEAM=${pl.environment['FLUTTER_XCODE_DEVELOPMENT_TEAM']}");
- command.add("PROVISIONING_PROFILE_SPECIFIER=${pl.environment['FLUTTER_XCODE_PROVISIONING_PROFILE_SPECIFIER']}");
- }
- final Process proc = await pm.start(command, workingDirectory: dialogDir.path);
- final int exitCode = await proc.exitCode;
- if (exitCode != 0) {
- fail('Command "$command" failed with exit code $exitCode.');
- }
- });
- return HealthCheckResult.success('close iOS dialog');
-}
-
-/// Result of a health check for a specific parameter.
-class HealthCheckResult {
- HealthCheckResult.success(this.name, [this.details]) : succeeded = true;
- HealthCheckResult.failure(this.name, this.details) : succeeded = false;
- HealthCheckResult.error(this.name, dynamic error, dynamic stackTrace)
- : succeeded = false,
- details = 'ERROR: $error\n${stackTrace ?? ''}';
-
- final String name;
- final bool succeeded;
- final String? details;
-
- @override
- String toString() {
- final StringBuffer buf = StringBuffer(name);
- buf.writeln(succeeded ? 'succeeded' : 'failed');
- if (details != null && details!.trim().isNotEmpty) {
- buf.writeln();
- // Indent details by 4 spaces
- for (String line in details!.trim().split('\n')) {
- buf.writeln(' $line');
- }
- }
- return '$buf';
- }
-}
-
-/// Check healthiness for discovered devices.
-Future<Map<String, Map<String, dynamic>>> healthcheck(Map<String, List<HealthCheckResult>> deviceChecks) async {
- final Map<String, Map<String, dynamic>> healthcheckMap = <String, Map<String, dynamic>>{};
- if (deviceChecks.isEmpty) {
- healthcheckMap[kAttachedDeviceHealthcheckKey] = <String, dynamic>{
- 'status': false,
- 'details': kAttachedDeviceHealthcheckValue,
- };
- } else {
- healthcheckMap[kAttachedDeviceHealthcheckKey] = <String, dynamic>{'status': true, 'details': null};
- }
- for (String deviceID in deviceChecks.keys) {
- final List<HealthCheckResult> checks = deviceChecks[deviceID]!;
- for (HealthCheckResult healthCheckResult in checks) {
- final Map<String, dynamic> healthCheckResultMap = <String, dynamic>{
- 'status': healthCheckResult.succeeded,
- 'details': healthCheckResult.details,
- };
- healthcheckMap[healthCheckResult.name] = healthCheckResultMap;
- }
- }
- return healthcheckMap;
-}
diff --git a/cipd_packages/device_doctor/lib/src/host_utils.dart b/cipd_packages/device_doctor/lib/src/host_utils.dart
deleted file mode 100644
index b51006b..0000000
--- a/cipd_packages/device_doctor/lib/src/host_utils.dart
+++ /dev/null
@@ -1,60 +0,0 @@
-// Copyright 2020 The Flutter 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:async';
-
-import 'dart:io';
-
-import 'package:process/process.dart';
-
-final RegExp _whitespace = RegExp(r'\s+');
-
-/// Kill all running processes on windows.
-///
-/// This is used when we first fail to list devices and need a retry.
-Future<bool> killAllRunningProcessesOnWindows(String processName, {ProcessManager? processManager}) async {
- processManager ??= LocalProcessManager();
- // Avoid endless loop when a process from a different use exists, and fails
- // to get killed every try.
- int tries = 3;
- while (tries > 0) {
- tries--;
- final pids = runningProcessesOnWindows(processName, processManager: processManager);
- if (pids.isEmpty) {
- return true;
- }
- for (String pid in pids) {
- processManager.runSync(<String>['taskkill', '/pid', pid, '/f']);
- }
- // Killed processes don't release resources instantenously.
- const Duration delay = Duration(seconds: 1);
- await Future<void>.delayed(delay);
- }
- return false;
-}
-
-List<String> runningProcessesOnWindows(String processName, {ProcessManager? processManager}) {
- processManager ??= LocalProcessManager();
- final ProcessResult result = processManager.runSync(<String>['powershell', 'Get-CimInstance', 'Win32_Process']);
- final List<String> pids = <String>[];
- if (result.exitCode == 0) {
- final String stdoutResult = result.stdout as String;
- for (String rawProcess in stdoutResult.split('\n')) {
- final String process = rawProcess.trim();
- if (!process.contains(processName)) {
- continue;
- }
- final List<String> parts = process.split(_whitespace);
-
- final String processPid = parts[0];
- final String currentRunningProcessPid = pid.toString();
- // Don't kill current process
- if (processPid == currentRunningProcessPid) {
- continue;
- }
- pids.add(processPid);
- }
- }
- return pids;
-}
diff --git a/cipd_packages/device_doctor/lib/src/ios_debug_symbol_doctor.dart b/cipd_packages/device_doctor/lib/src/ios_debug_symbol_doctor.dart
deleted file mode 100644
index 32d2b08..0000000
--- a/cipd_packages/device_doctor/lib/src/ios_debug_symbol_doctor.dart
+++ /dev/null
@@ -1,253 +0,0 @@
-// Copyright 2020 The Flutter 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:async';
-import 'dart:convert';
-import 'dart:io' as io;
-
-import 'package:args/command_runner.dart';
-import 'package:file/file.dart';
-import 'package:file/local.dart';
-import 'package:process/process.dart';
-import 'package:logging/logging.dart';
-import 'package:platform/platform.dart';
-
-class DiagnoseCommand extends Command<bool> {
- DiagnoseCommand({
- this.processManager = const LocalProcessManager(),
- Logger? loggerOverride,
- }) : logger = loggerOverride ?? Logger.root;
-
- final Logger logger;
-
- final ProcessManager processManager;
-
- final String name = 'diagnose';
- final String description = 'Diagnose whether attached iOS devices have errors.';
-
- Future<bool> run() async {
- final List<String> command = <String>['xcrun', 'xcdevice', 'list'];
- final io.ProcessResult result = await processManager.run(
- command,
- );
- if (result.exitCode != 0) {
- logger.severe(
- '$command failed with exit code ${result.exitCode}\n${result.stderr}',
- );
- return false;
- }
- final String stdout = result.stdout as String;
- logger.info(stdout);
- final Iterable<XCDevice> devices = XCDevice.parseJson(stdout);
- final Iterable<XCDevice> devicesWithErrors = devices.where((XCDevice device) => device.hasError);
-
- if (devicesWithErrors.isNotEmpty) {
- logger.severe('Found devices with errors!');
-
- for (final XCDevice device in devicesWithErrors) {
- logger.severe('${device.name}: ${device.error}');
- }
- return false;
- }
-
- return true;
- }
-}
-
-class RecoverCommand extends Command<bool> {
- RecoverCommand({
- this.processManager = const LocalProcessManager(),
- Logger? loggerOverride,
- this.fs = const LocalFileSystem(),
- this.platform = const LocalPlatform(),
- }) : logger = loggerOverride ?? Logger.root {
- argParser
- ..addOption(
- 'cocoon-root',
- help: 'Path to the root of the Cocoon repo. This is used to find the Build dashboard macos project, which is '
- 'then used to open Xcode.',
- mandatory: true,
- )
- ..addOption(
- 'timeout',
- help: 'Integer number of seconds to allow Xcode to run before killing it.',
- defaultsTo: '300',
- );
- }
-
- final Logger logger;
- final ProcessManager processManager;
- final FileSystem fs;
- final Platform platform;
-
- final String name = 'recover';
- final String description = 'Open Xcode UI to allow it to sync debug symbols from the iPhone';
-
- /// Xcode Project workspace file for the build dashboard Flutter app.
- ///
- /// Should be located at //cocoon/dashboard/ios/Runner.xcodeproj/project.xcworkspace.
- Directory get dashboardXcWorkspace {
- final String cocoonRootPath = argResults!['cocoon-root'];
- final Directory cocoonRoot = fs.directory(cocoonRootPath);
- final Directory dashboardXcWorkspace = cocoonRoot
- .childDirectory('dashboard')
- .childDirectory('ios')
- .childDirectory('Runner.xcodeproj')
- .childDirectory('project.xcworkspace')
- .absolute;
- if (!dashboardXcWorkspace.existsSync()) {
- throw StateError(
- 'You provided the --cocoon-root option with "$cocoonRootPath", and the device doctor tried to '
- "locate the build dashboard's project.xcworkspace directory at \"${dashboardXcWorkspace.path}\" "
- 'but that path does not exist on disk.',
- );
- }
- return dashboardXcWorkspace;
- }
-
- @override
- Future<bool> run() async {
- final int? timeoutSeconds = int.tryParse(argResults!['timeout']);
- if (timeoutSeconds == null) {
- throw ArgumentError('Could not parse an integer from the option --timeout="${argResults!['timeout']}"');
- }
-
- _deleteSymbols();
-
- // Prompt Xcode to first setup without opening the app.
- // This will return very quickly if there is no work to do.
- logger.info('Running Xcode first launch...');
- final io.ProcessResult runFirstLaunchResult = await processManager.run(<String>[
- 'xcrun',
- 'xcodebuild',
- '-runFirstLaunch',
- ]);
- final String runFirstLaunchStdout = runFirstLaunchResult.stdout.trim();
- if (runFirstLaunchStdout.isNotEmpty) {
- logger.info('stdout from `xcodebuild -runFirstLaunch`:\n$runFirstLaunchStdout\n');
- }
- final String runFirstLaunchStderr = runFirstLaunchResult.stderr.trim();
- if (runFirstLaunchStderr.isNotEmpty) {
- logger.info('stderr from `xcodebuild -runFirstLaunch`:\n$runFirstLaunchStderr\n');
- }
- final int runFirstLaunchCode = runFirstLaunchResult.exitCode;
- if (runFirstLaunchCode != 0) {
- logger.info('Failed running `xcodebuild -runFirstLaunch` with code $runFirstLaunchCode!');
- return false;
- }
-
- final Duration timeout = Duration(seconds: timeoutSeconds);
- logger.info('Launching Xcode...');
- final Future<io.ProcessResult> xcodeFuture = processManager.run(<String>[
- 'open',
- '-n', // Opens a new instance of the application even if one is already running
- '-F', // Opens the application "fresh," without restoring windows
- '-W', // Wait for the opened application (Xcode) to close
- dashboardXcWorkspace.path,
- ]);
-
- unawaited(
- xcodeFuture.then((io.ProcessResult result) {
- logger.info('Open closed...');
- final String stdout = result.stdout.trim();
- if (stdout.isNotEmpty) {
- logger.info('stdout from `open`:\n$stdout\n');
- }
- final String stderr = result.stderr.trim();
- if (stderr.isNotEmpty) {
- logger.info('stderr from `open`:\n$stderr\n');
- }
- if (result.exitCode != 0) {
- throw Exception('Failed opening Xcode!');
- }
- }),
- );
-
- logger.info('Waiting for $timeoutSeconds seconds');
- await Future.delayed(timeout);
- logger.info('Waited for $timeoutSeconds seconds, now killing Xcode');
- final io.ProcessResult result = await processManager.run(<String>['killall', '-9', 'Xcode']);
-
- if (result.exitCode != 0) {
- logger.severe('Failed killing Xcode!');
- return false;
- }
- return true;
- }
-
- /// Delete all symbols by deleting the `iOS DeviceSupport` directory.
- /// Xcode will regenerate this folder and symbols for connected devices
- /// when Xcode is opened.
- void _deleteSymbols() {
- final String? home = platform.environment['HOME'];
- if (home == null) {
- logger.warning('\$HOME path was not found');
- return;
- }
- final Directory deviceSupportDirectory = fs.directory('$home/Library/Developer/Xcode/iOS DeviceSupport');
- if (!deviceSupportDirectory.existsSync()) {
- logger.warning('iOS Device Support directory was not found at ${deviceSupportDirectory.path}');
- return;
- }
- logger.info('Deleting iOS DeviceSupport...');
- try {
- deviceSupportDirectory.deleteSync(recursive: true);
- } on FileSystemException catch (e) {
- // Error that indicates why files cannot be deleted, such as
- // another program having the files open.
- logger.severe('${e.message}: ${e.osError}');