[wiki migration] Remaining pages under docs/contributing/ (#148790)
This commit is contained in:
74
docs/contributing/Chat.md
Normal file
74
docs/contributing/Chat.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# Chatting on Discord
|
||||
|
||||
<img src="https://github.com/flutter/flutter/assets/551196/1381071b-e482-4e07-b096-60e9219d3ff7" width=300 align=right alt="">
|
||||
|
||||
The Flutter team uses a [Discord server](https://discord.com/channels/608014603317936148). [This is invite link for Flutter's Discord server](https://discord.gg/ht477J5PyH). Please do not share either link directly, instead share links to this page.
|
||||
|
||||
The server is open to the public, though some channels are intended only for people who are actively contributing. **See the #welcome channel for instructions on posting to the server (you won't be able to see the channels until you acknowledge the rules there).**
|
||||
|
||||
We recommend you use the same display name on Discord and GitHub.
|
||||
|
||||
(Our Flutter Discord server is unrelated to the [r/FlutterDev Discord server](https://www.reddit.com/r/FlutterDev/), which is where the r/FlutterDev community shares their apps, discusses Flutter, and so on. When in doubt, remember: our server has Dash as an icon!)
|
||||
|
||||
## Existing channels
|
||||
|
||||
We have different channels for different purposes (this list is not exhaustive):
|
||||
|
||||
| Channel | Description | Participants |
|
||||
| - | - | - |
|
||||
| #welcome | Welcome message. | Only admins can post.<br>Anyone can read.
|
||||
| #announcements | Flutter announcements (e.g. breaking changes). | Only team members can post (but not frequently).<br>Anyone can read.
|
||||
| #server-support | Forum for asking for your role to be changed, for code of conduct violations to be raised, and for other administrative issues. | Anyone.
|
||||
| #general | Chat about anything related to Flutter. | Anyone.
|
||||
| #help | People asking for our help. | Anyone.
|
||||
| #package-authors | For people who develop Flutter packages. | People writing packages for [pub.dev](https://pub.dev/).
|
||||
| #dart | Questions about the Dart language. | Anyone.
|
||||
| #tree-status | Announcements about whether the trees are open or closed. | Anyone, but discussion should happen elsewhere.
|
||||
| #hackers | Chat about anything related to Flutter. | Flutter contributors.<br>Anyone can watch.
|
||||
| #hackers-* | Chat specifically about foo, where foo is something to do with Flutter development, for example "engine", "framework", "desktop", "devtools", etc. | Flutter contributors.<br>Anyone can watch.
|
||||
| #hackers-dart | Questions about the Dart language. | Flutter contributors.<br>Anyone can watch.
|
||||
| #hackers-new | New people to the team and people who want to help them. | Flutter contributors.<br>Anyone can watch.
|
||||
| #hackers-triage | For use while triaging bugs. | Flutter contributors.<br>Anyone can watch.
|
||||
| #hidden-chat | Chat about anything related to Flutter. | Flutter contributors only.<br>Not public.
|
||||
| general (voice) | Talk (audio) about anything related to Flutter. | Anyone.
|
||||
| team (voice) | Talk (audio) that is only open to team members. | Flutter contributors.
|
||||
|
||||
The #hidden-chat and #team channels are not publicly-readable, you have to be a member of the "team member" role to see them.
|
||||
|
||||
## New channels
|
||||
|
||||
If you need a new channel, first just use #general or #hackers, or a thread in one of those channels; if the conversation lasts more than a day, ask for a channel in #server-support.
|
||||
|
||||
If you find your team's channel is hard to follow due to having too many topics discussed at once, ask for a new channel, or use threads.
|
||||
|
||||
If you would like a channel for subcommunities, e.g. #women or #china, that should be fine. (Subcommunities that are already overrepresented in the main channels, e.g. #english-speakers, are less likely to be good candidates for dedicated channels.)
|
||||
|
||||
For practical reasons, we do not use Discord for chat groups limited to specific customers. For example, if your company wants a private discussion channel with the Flutter team, we would not use Discord.
|
||||
|
||||
Each channel describes its topic in the channel description. Please read the channel description before posting in a channel, to make sure you're using the appropriate one.
|
||||
|
||||
## Policies
|
||||
|
||||
Our [code of conduct](https://github.com/flutter/flutter/blob/main/CODE_OF_CONDUCT.md) applies to the Discord server, as it does to any communications involving Flutter.
|
||||
|
||||
The #hackers-* channels are visible to anyone, but please only post in those channels if you are actively contributing. If you want help with your app, ask in #help instead. If you want to learn how to contribute, have a look at our [contributing guide](https://github.com/flutter/flutter/blob/main/CONTRIBUTING.md).
|
||||
|
||||
See the [contributor access](Contributor-access.md) wiki page for details on becoming a member of the "team" role.
|
||||
|
||||
Please don't direct-message people unless they are comfortable with it (ask publicly first).
|
||||
You can disable direct messages on this server by changing your Privacy settings for the server, or on a global basis by changing your Privacy & Safety user settings.
|
||||
|
||||
## Discord features
|
||||
|
||||
### Threading
|
||||
|
||||
Discord has both actual threads (temporary new channels) and a threadingish feature called [Replies](https://support.discord.com/hc/en-us/articles/360057382374-Replies-FAQ) that lets you tie messages to earlier messages and notify the original commenter.
|
||||
|
||||
### Status
|
||||
|
||||
You can [change your status](https://support.discord.com/hc/en-us/articles/360035407531-Custom-Status) (online, away, custom messages) by clicking on your avatar in Discord.
|
||||
|
||||
|
||||
# Design documents
|
||||
|
||||
This page used to discuss how to create design docs, but that content is now on its own page: [Design documents](Design-Documents.md).
|
||||
139
docs/contributing/Contributor-access.md
Normal file
139
docs/contributing/Contributor-access.md
Normal file
@@ -0,0 +1,139 @@
|
||||
For people who make the occasional contribution to Flutter (filing an issue, submitting the occasional PR, chatting on Discord), the default set of permissions is fine. However, if you are a frequent contributor, whether helping us in triage, or often fixing bugs, or regularly improving our documentation, or regularly helping others in our #help channel, or participating in high-level design discussions and prioritization, you may find your life is more pleasant with commit access (also known as "contributor access", "being a member of the flutter-hackers group", "being a member of the Flutter team").
|
||||
|
||||
We grant commit access (which includes full rights to the issue database, such as being able to edit labels, and grants access to our internal chat channels) to people who have gained our trust and demonstrated a commitment to Flutter.
|
||||
|
||||
Specifically, if you meet one of the following criteria and you have a sponsor (someone who already has contributor access and agrees that you should be granted access), then please ask your sponsor to propose, on the #server-support [Chat](Chat.md) channel, that you be made a member of the team, and then reply to that message explaining which criteria below you are claiming to meet. The possible criteria are:
|
||||
|
||||
* You have a long history of participating productively, e.g. in our [Chat](Chat.md) channels, helping with [Triage](https://github.com/flutter/flutter/wiki/Triage), helping other contributors track down problems, finding meaningful issues in submitted PRs, helping people in our #help channel, etc, all while demonstrating exemplary behavior that closely aligns with our [code of conduct](https://github.com/flutter/flutter/blob/main/CODE_OF_CONDUCT.md).
|
||||
* You have recently submitted several PRs that have landed successfully (received an LGTM, PR was merged, no regressions reported, PR was not reverted), without needing extensive tutoring in the process.
|
||||
* You are employed by a company with a history of contributing to Flutter, for the purpose of yourself regularly contributing to Flutter.
|
||||
* You represent a development team that creates applications, plugins, or packages using Flutter and have a close relationship with our developer relations team, including having a customer label, and have a great need to regularly update labels on issues (see [Issue hygiene, Customers](./issue_hygiene/README.md#customers)). (This is rare.)
|
||||
|
||||
Being granted access means that you will be added to the "flutter-hackers" group on GitHub and the "team" role on Discord. This privilege is granted with some expectation of responsibility: contributors are people who care about Flutter and want to help Flutter along our [roadmap](https://github.com/flutter/flutter/wiki/Roadmap). A contributor is not just someone who can make changes or comment on issues, but someone who has demonstrated their ability to collaborate with the team, get the most knowledgeable people to review code, contribute high-quality code, follow through to fix bugs (in code or tests), and provide meaningful insights on issues.
|
||||
|
||||
We grant access optimistically based on a reasonably small volume of evidence of good faith. Correspondingly, we will remove access quickly if we find our trust has been violated. Contributors with commit access must still follow all our processes and policies, and must follow our [code of conduct](https://github.com/flutter/flutter/blob/main/CODE_OF_CONDUCT.md) rigorously. (Please read it, it's stricter than most.)
|
||||
|
||||
|
||||
### Responsibilities
|
||||
|
||||
#### Code of conduct
|
||||
|
||||
If you have commit access or "team" access on the Discord server, you are responsible for enforcing our [code of conduct](https://github.com/flutter/flutter/blob/main/CODE_OF_CONDUCT.md).
|
||||
|
||||
Our code of conduct is much, much stricter than most. We do not wait until someone has been actively rude or insulting. Being disrespectful in any way is grounds for action. For example, passive-aggressive whining and general unconstructive negativity are all violations of the code of conduct. If someone is in a bad mood, we would rather they avoided contributing to Flutter on that day.
|
||||
|
||||
When you see something that might be described as unwelcoming or is in some other way a violation of our code of conduct, promptly contact the offender and ask them to read the code of conduct and consider how they might more effectively espouse its philosophy. Most people react very positively to this.
|
||||
|
||||
If they react negatively, or if they continue to make the environment unpleasant, they should be removed from the environment. On Discord, this would be kicking them from the channel. Repeat offenders should be banned. On GitHub, they can be blocked from our organisation (you may need to ask @Hixie or another admin of our GitHub org to do this). Please let the #server-support [Chat](Chat.md) channel know when you do anything like this, so that we can keep an eye on how common it is.
|
||||
|
||||
#### Maintaining documentation
|
||||
|
||||
Part of being a contributor is making sure our documentation is up to date, including our internal (team-facing) documentation such as this wiki. If you spot something wrong, please fix it! As a contributor, you have access to the wiki.
|
||||
|
||||
For the wiki specifically, since there's no code review, it's good to mention on Discord that you've made a change when you make one.
|
||||
|
||||
### Privileges
|
||||
|
||||
Being in the GitHub "flutter-hackers" group gives you the following:
|
||||
|
||||
* The ability to merge your own PRs once they are reviewed (see [Tree Hygiene](Tree-hygiene.md)).
|
||||
|
||||
* The ability to add labels, milestones, etc, on issues on GitHub (see [Issue Hygiene](./issue_hygiene/README.md)).
|
||||
|
||||
* PRs will run their tests slightly faster.
|
||||
|
||||
Being in the Discord "team" group gives you the following:
|
||||
|
||||
* The ability to talk without rate-limiting on the #hackers-* channels.
|
||||
|
||||
* The ability to kick people.
|
||||
|
||||
* The ability to manage the server emoji.
|
||||
|
||||
|
||||
## Process
|
||||
|
||||
The actual process (as followed by Flutter repo admins) is as follows:
|
||||
|
||||
1. Verify that they qualify under all the terms described above. Make sure they have a sponsor who isn't you.
|
||||
1. Verify the identity of the person making the request. Ask them to confirm, on Discord, that they have read the style guide, issue or tree hygiene wiki page, code of conduct, and other documents relevant to them.
|
||||
1. Add them to our private spreadsheet (go/flutter-organization-members).
|
||||
1. Click the "Add a member" button on [the flutter-hackers team page](https://github.com/orgs/flutter/teams/flutter-hackers/members) on GitHub.
|
||||
1. Type their name in the text field, select them, then click the "Invite" button.
|
||||
1. Add them to the "team" group on Discord. Be sure to verify that you are promoting the right person; multiple people can have the same nickname on Discord!
|
||||
|
||||
*For new Googlers joining the team*: You need to ask someone in the team to add you to get added. It's not an automatic process after you join the flutter Google group.
|
||||
|
||||
|
||||
## Inactivity
|
||||
|
||||
We occasionally check for account with commit access that have not been used for a while. It takes very little to count as "active" (e.g. commenting on an issue, even adding an emoji reaction to an issue). If your account has been inactive for over a year we will try to reach out (e.g. by e-mail or on Discord) before removing access.
|
||||
|
||||
If your account access was removed but you wish to return to contributing to Flutter, you are most welcome to do so; just reach out on the Discord (see [Chat](Chat.md)) and ask someone to renominate you according to the process described above.
|
||||
|
||||
|
||||
# Access rights to Flutter dashboard
|
||||
|
||||
The [Flutter dashboard](https://flutter-dashboard.appspot.com/) shows what recently landed and what tests passed or failed with those commits. To see rerun tasks, you need to be added to an allowlist. Anyone with commit access is eligible to be added to that allowlist, but only certain team members have the permissions required to update the backend database where the permissions are stored. To get access, ask on #hackers-infra to be added to the allowlist.
|
||||
|
||||
## Adding a contributor to Flutter Dashboard
|
||||
|
||||
*This is only for team members with access to the Flutter Dashboard Datastore.*
|
||||
|
||||
1. Open [flutter-dashboard datastore](https://console.cloud.google.com/datastore/entities;kind=AllowedAccount;ns=__$DEFAULT$__/query/kind?project=flutter-dashboard)
|
||||
2. Click `Create Entity`
|
||||
3. Click `Email: Empty` -> Edit property. Insert contributor Google account
|
||||
4. Click `Create`
|
||||
|
||||
# Access to LUCI recipes and configuration repositories
|
||||
|
||||
If you need access to the LUCI recipes, you need to be added to the relevant ACLs. Ask in #hackers-infra to be added to the LUCI ACLs.
|
||||
|
||||
## Process
|
||||
|
||||
A Googler has to be the one to grant permission. Documentation on how to use the relevant tools is available at: https://goto.google.com/gob-ctl#add-or-remove-users-in-host-acl
|
||||
|
||||
# Access to Flutter Gold
|
||||
|
||||
If you need access to triage images in [Flutter Gold](https://flutter-gold.skia.org/), you need to be added as an authorized user.
|
||||
Users in the `@google.com` domain are already authorized to use Flutter Gold, but `@gmail.com` addresses can also be added to the allow list.
|
||||
|
||||
## Process
|
||||
The list of authorized users is maintained in the [skia build-bot repository](https://skia.googlesource.com/buildbot), in [this file](https://skia.googlesource.com/buildbot/+/refs/heads/main/golden/k8s-instances/flutter/flutter-skiacorrectness.json5). Googlers can submit a change to add to the authorized users.
|
||||
|
||||
This repository is also [mirrored on GitHub.](https://github.com/google/skia-buildbot)
|
||||
|
||||
# fcontrib.org accounts
|
||||
|
||||
If you are a team member who wants to share design docs (see [Chat](Chat.md)) but you don't want to use your own personal account, you can ask a Flutter admin for an fcontrib.org account. Ping @Hixie or another admin in the #server-support channel on Discord.
|
||||
|
||||
## Process
|
||||
|
||||
You’ll need the user’s email account somewhere else, first and last name, and desired fcontrib.org account login before you begin.
|
||||
|
||||
To add a fcontrib.org participant:
|
||||
1. Open an incognito window and log in using _your_ admin fcontrib credentials at https://admin.google.com/. (q.v. valentine)
|
||||
2. Under “Users” in the upper left of the main content area, click “Add a User” and follow the prompts.
|
||||
3. For a password, choose “Generate Password” and email the password to the new account holder using their non-fcontrib account -- they’ll be able to log in with that and then choose a new password.
|
||||
|
||||
# Review teams
|
||||
|
||||
Some parts of the codebase have teams specified so that PRs get round-robin assigned for review.
|
||||
|
||||
To join one of these teams, request members be added/deleted, or change any settings, ping @Hixie on Discord. Members must be a member of the Flutter Hackers group (as documented at the top of this page).
|
||||
|
||||
We currently have the following review teams:
|
||||
|
||||
* [`android-reviewers`](https://github.com/orgs/flutter/teams/android-reviewers): for folks working on the Android port of Flutter; use `#hackers-android` for discussions.
|
||||
* [`devtools-reviewers`](https://github.com/orgs/flutter/teams/devtools-reviewers): for the [devtools](https://github.com/flutter/devtools) repo; use `#hackers-devexp` for discussions.
|
||||
* [`website-reviewers`](https://github.com/orgs/flutter/teams/website-reviewers): for folks working on www.flutter.dev and docs.flutter.dev; use `#hackers-devrel` for discussions.
|
||||
|
||||
To create a new team, contact @Hixie. You will also need to create a `CODEOWNERS` file to actually trigger the review assignment.
|
||||
|
||||
# Pusher permissions
|
||||
|
||||
Some branches are protected to avoid accidents. Only people in the specific branches can push to them. Anyone can ask to be added or removed from these groups, they exist only to reduce accidents, not for security.
|
||||
|
||||
To join one of these teams, request members be added/deleted, or change any settings, ping @Hixie on Discord. Members must be a member of the Flutter Hackers group (as documented at the top of this page).
|
||||
|
||||
The following groups have been defined for these purposes: pushers-beta, pushers-fuchsia
|
||||
535
docs/contributing/Data-driven-Fixes.md
Normal file
535
docs/contributing/Data-driven-Fixes.md
Normal file
@@ -0,0 +1,535 @@
|
||||
# Data-driven Fixes
|
||||
|
||||
_This documentation is preliminary. Suggestions for improvements are welcome. Do not edit this page directly, it is copied from an internal source._
|
||||
|
||||
Data-driven fixes is a feature that allows the Flutter package authors to provide data about changes that have been made to the package's public API and uses that data to help users of the package update their code when updating to a newer version of the package. It is not supported for other package authors.
|
||||
|
||||
The data is used by two tools:
|
||||
- The analysis server uses it to provide quick fixes (also known as code actions) in the IDE.
|
||||
- The `dart fix` command uses it to apply bulk edits to the code in a file or directory.
|
||||
|
||||
The purpose of this document is to describe how to write and test the data used by these tools. It contains four sections:
|
||||
- [Overview](#overview) provides an overview of how this feature is intended to be used and what kind of data can be expressed.
|
||||
- [Examples](#examples) provides examples of how to express some common kinds of changes.
|
||||
- [Reference](#reference) provides a complete definition of the data and how that data is used to update client code.
|
||||
- [Testing](#testing) provides a guide for how this data can be tested.
|
||||
|
||||
|
||||
## Overview
|
||||
|
||||
The data used by this feature describes the changes made to the public API of a package. It's contained in a [data file](#data-file) in the package that defines the API.
|
||||
|
||||
The data is organized around the individual elements of the API, where an [element](#element) is anything that can be referenced outside the library in which it's declared. This includes top-level declarations, such as classes and extensions, and their members, such as methods and fields.
|
||||
|
||||
The changes to an element that can be described are designed to be both fine-grained and high-level.
|
||||
|
||||
By "fine-grained" we mean that the overall change to an API is described in terms of small, composable changes. For example, instead of describing all of the ways in which a method's signature has changed, there are individual changes for adding and removing individual parameters, renaming parameters, etc. This reduces the complexity of describing the overall change.
|
||||
|
||||
By "high-level" we mean that the changes are described in terms of how the API changed, not in terms of what needs to be done to client code in response to those changes. For example, if a parameter is added to a method the data would describe the parameter and the value to be used in invocations, but doesn’t describe how to find invocations or how to find overrides. It's the responsibility of the tools to figure out how to modify the client code in response to those changes. This reduces the amount of work required of package authors initially and allows the tools to adjust as the Dart language evolves, removing the need for package authors to refine the data.
|
||||
|
||||
The description of an [element](#element) and the [change](#change)s to that element are encapsulated in a [transform](#transform) and the [data file](#data-file) is essentially a list of [transform](#transform)s.
|
||||
|
||||
The changes are applied when an analysis of the client code produces diagnostics. For example, if a method is renamed then invocations of the old method will produce a diagnostic indicating that the method isn’t defined. This also works if the old method is still declared and marked as being deprecated because a diagnostic is also produced in that case. Because of that, we encourage you to add data for an API when the API is first deprecated. If the API doesn’t go through a deprecation period, then the changes should be added to the data when the changes are made.
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
This section provides examples of how to express some common kinds of changes.
|
||||
|
||||
Each example includes the API being changed, both the old API marked as deprecated and the new API that replaced it. As noted in the [Overview](#overview), it isn’t necessary for the old API to still be declared in order for the tools to be able to apply the changes, but it’s included in the examples for clarity.
|
||||
|
||||
As a result of always including both the old and new APIs in the examples, the elements are always renamed. It isn’t necessary to rename an element in order for the tools to be able to apply the changes.
|
||||
|
||||
### Rename a method
|
||||
|
||||
One of the most common API changes is to rename an element. In this section we'll show how to specify that a method has been renamed. Let's assume that your package defines a class `C` in the file `lib/c.dart` with a renamed method similar to the following:
|
||||
|
||||
```dart
|
||||
class C {
|
||||
@deprecated
|
||||
int oldName(String s) => newName(s);
|
||||
|
||||
int newName(String s) { ... }
|
||||
}
|
||||
```
|
||||
|
||||
In the data file we need to specify which method was renamed, what it's old name was, and what it's new name is. We do that by writing a data file like the following:
|
||||
|
||||
```yaml
|
||||
version: 1
|
||||
transforms:
|
||||
- title: 'Rename to newName'
|
||||
date: 2020-11-20
|
||||
element:
|
||||
uris: ['c.dart']
|
||||
method: 'oldName'
|
||||
inClass: 'C'
|
||||
changes:
|
||||
- kind: 'rename'
|
||||
newName: 'newName'
|
||||
```
|
||||
|
||||
This tells the tools that any references to the method `C.oldName` should be updated to refer to the method `C.newName`. That includes both invocations of the method as well as references where the method is being torn off. It also tells the tool that any methods that used to override the old method need to be renamed so that they now override the new method.
|
||||
|
||||
|
||||
## Reference
|
||||
|
||||
This section provides a complete definition of what data can be included in a [data file](#data-file) and how that data is used to update client code.
|
||||
|
||||
In the following subsections, the text `...` is a placeholder for a value that is described in a different subsection.
|
||||
|
||||
### data file
|
||||
|
||||
A _data file_ contains the data used to update clients of a package when the public API of the package has been changed. The data is represented by a single [transform set](#transform-set).
|
||||
|
||||
The data is stored in a file named `fix_data.yaml` in the `lib` directory of the package. There is a single data file per package.
|
||||
|
||||
You might find it useful to include a comment at the beginning of the file that has a link to this documentation for easy reference.
|
||||
|
||||
### transform set
|
||||
|
||||
A _transform set_ describes a set of changes made to the API of a single package. It's represented as a map with two keys: `version` and `transforms`.
|
||||
|
||||
The `version` key is required. The value of the `version` key is an integer used to identify the version of the file's content. The version described by this document is version `1`. The version key is typically the first line of the file.
|
||||
|
||||
The version key lets the tooling continue to work with older versions of data files when the format of the file needs to be changed, and allows older versions of the SDK determine when a package is using a newer and not understood format.
|
||||
|
||||
The `transforms` key is required. The value of the `transforms` key is a list of [transform](#transform) objects. While the list is allowed to be empty, there’s no value in having a data file unless there's at least one transform.
|
||||
|
||||
For example, the basic content of a data file generally looks like this:
|
||||
|
||||
```yaml
|
||||
version: 1
|
||||
transforms:
|
||||
- ...
|
||||
- ...
|
||||
```
|
||||
|
||||
The transforms can be in any order, but to make it easier to maintain the file we recommend putting newer changes at the beginning of the file, just like you would in a change log. You might find it useful to use comments to group sets of transforms, possibly based on the published version of the package in which the changes were made.
|
||||
|
||||
### transform
|
||||
|
||||
A _transform_ describes a set of changes made to a single element in the API. It's represented as a map with seven keys: `title`, `date`, `bulkApply`, `element`, `oneOf`, `changes`, and `variables`.
|
||||
|
||||
The `title` key is required. The value of the `title` key is a string that is displayed in the IDE to describe the changes that will be made to the user's code. It's usually shown as a label in a menu, so it should be kept short. And because most APIs can be referenced in multiple ways, the description should be general enough to cover all of those cases. For example, if a method is renamed then the name will need to be updated both at invocation sites and anywhere the old method was being overridden. Using a title like “Invoke newMethod” wouldn't be appropriate for updating an override, so a better title would be “Rename to newMethod”, which would work for both cases.
|
||||
|
||||
The `date` key is required. The value of the `date` key is a string encoding of a date. The format of the string is required to be the same as the format accepted by the method [`DateTime.parse`](https://api.dart.dev/dart-core/DateTime/parse.html).
|
||||
|
||||
The `bulkApply` key is optional. The value of the `bulkApply` key is a Boolean value indicating whether the transform should be applied when bulk fixes are being made. The default value is `true`. You need to disallow a transform from being used in a bulk fix tool in cases where there are multiple transforms for a single API. If there's a clear default, then you can allow that one transform to be applied.
|
||||
|
||||
The `element` key is required. The value of the `element` key is an [element](#element) object. It specifies the element in the API that was changed, such as a class or a method.
|
||||
|
||||
Either the `oneOf` key or the `changes` key is required, but it isn't valid to have both. These keys are two different ways to specify the list of changes that will be applied when a reference to the element is found.
|
||||
|
||||
The value of the `oneOf` key is a list of [conditional change](#conditional-change) objects. For each reference to the element that's found, the first conditional change whose condition is true will determine the list of changes to be applied to that reference. If more than one conditional change has a condition that's true, only the first one will be applied. If none of the conditions is true, then no changes will be applied.
|
||||
|
||||
The value of the `changes` key is a list of [change](#change) objects. The list of changes is unconditionally selected as the changes that will be applied.
|
||||
|
||||
The `variables` key is optional.The value of the `variables` key is a [variable map](#variable-map).
|
||||
|
||||
For example, you can specify a transform like this:
|
||||
|
||||
```yaml
|
||||
title: 'Descriptive title'
|
||||
date: 2020-09-14
|
||||
element: ...
|
||||
changes:
|
||||
- ...
|
||||
- ...
|
||||
```
|
||||
|
||||
You might find it useful to include a comment to provide more information about each transform, such as design documents, issues, or even PRs that motivated or implemented the change.
|
||||
|
||||
### element
|
||||
|
||||
An _element_ describes a single element in the API that was changed, such as a class or a method. It's represented as a map with either two or three keys depending on the kind of element being described. All elements have a `uris` key and a second key that specifies the type and name of the element, such as `class` or `method`. Elements that are members of a top-level element have a third key specifying the containing element.
|
||||
|
||||
The `uris` key is required. The value of the `uris` key is a list of [uri](#uri)s, each of which is the URI of a public library that includes the element (or the container of the element in the case of members such as methods).
|
||||
|
||||
The second key, which is required, indicates the kind and name of the element. The kind is given by the key itself and the name is given as the value of the second key, which is a string. The possible keys, and hence kinds, are:
|
||||
- class
|
||||
- constant (from an enum)
|
||||
- constructor
|
||||
- enum
|
||||
- extension
|
||||
- field
|
||||
- function (top-level)
|
||||
- getter
|
||||
- method
|
||||
- mixin
|
||||
- setter
|
||||
- typedef
|
||||
- variable (top-level)
|
||||
|
||||
If the element is a member of a top-level element, then the third key is required and it indicates the kind and name of the top-level element containing the member. The possible container keys are:
|
||||
- inClass
|
||||
- inEnum
|
||||
- inExtension
|
||||
- inMixin
|
||||
|
||||
For example, you can specify a class named `C`:
|
||||
|
||||
```yaml
|
||||
uris: ['lib.dart']
|
||||
class: 'C'
|
||||
```
|
||||
|
||||
or specify a method named `m` in a class named `C`:
|
||||
|
||||
```yaml
|
||||
uris: ['lib.dart']
|
||||
method: 'm'
|
||||
inClass: 'C'
|
||||
```
|
||||
|
||||
To specify the unnamed constructor in a class, use an empty string for the name of the constructor:
|
||||
|
||||
```yaml
|
||||
uris: ['lib.dart']
|
||||
constructor: ''
|
||||
inClass: 'C'
|
||||
```
|
||||
|
||||
Despite the fact that the names of named parameters are used by clients to associate an argument with the parameter, they aren't considered separate API elements and therefore can't be described by an element object. If you have modified the parameters of a method or function, then the element that was changed is the method or function associated with the parameter.
|
||||
|
||||
### conditional change
|
||||
|
||||
A _conditional change_ is a list of [change](#change)s that is applied only when a condition is true. It's represented as a map with two keys: `if` and `changes`.
|
||||
|
||||
The `if` key is required. The value of the `if` key is a [condition](#condition). Any variables declared for the whole transform can be referenced in the condition.
|
||||
|
||||
The `changes` key is required. The value of the `changes` key is a list of [change](#change) objects.
|
||||
|
||||
### condition
|
||||
|
||||
A _condition_ is a Boolean-valued expression. It's represented by a string that uses a subset of Dart’s expression syntax to express the condition. The currently supported syntax is:
|
||||
```
|
||||
<condition> ::= <logicalAndExpression>
|
||||
|
||||
<logicalAndExpression> ::= <equalityExpression> ('&&' <equalityExpression>)*
|
||||
|
||||
<equalityExpression> ::= <primary> (<equalityOperator> <primary>)?
|
||||
|
||||
<equalityOperator> ::= '==' | '!='
|
||||
|
||||
<primary> ::= <variableName> | <stringLiteral>
|
||||
```
|
||||
|
||||
where `<variableName>` is the name of a variable from a [variable map](#variable-map) and `<stringLiteral>` is a single-line string literal with single quote delimiters.
|
||||
|
||||
### change
|
||||
|
||||
A _change_ describes a single change that was made to the element. It's represented as a map. The number and names of the keys depends on the kind of change being described, but all changes have a `kind` key.
|
||||
|
||||
The `kind` key is required. The value of the `kind` key is a string indicating the kind of change. The valid kinds are:
|
||||
- [addParameter](#addParameter)
|
||||
- [addTypeParameter](#addTypeParameter)
|
||||
- [removeParameter](#removeParameter)
|
||||
- [rename](#rename)
|
||||
- [renameParameter](#renameParameter)
|
||||
- [changeParameterType](#changeParameterType)
|
||||
- [replacedBy](#replacedBy)
|
||||
|
||||
Individual kinds of changes are described in the section below, titled by the kind.
|
||||
|
||||
#### addParameter
|
||||
|
||||
An _add parameter_ change indicates that a parameter was added to a function or method. It has five keys: `kind`, `index`, `name`, `style` and `argumentValue`.
|
||||
|
||||
The `index` key is required. The value of the `index` key is the zero-based index of the parameter after all the changes related to parameters have been applied. It's used to know:
|
||||
- where to add the parameter in method overrides. If parameter `p` is to be add to a method `m` in a class `A`, and the user has a subclass of `A` that overrides the method `m`, the tool will add the parameter at `index` to the method `m` in the subclass.
|
||||
- if the parameter is a positional parameter, which argument to remove at each invocation site.
|
||||
|
||||
The `name` key is required. The value of the `name` key is the name of the parameter. It's used as the name of new parameters added in overrides and, if the parameter is a named parameter, as the name of the argument at invocation sites.
|
||||
|
||||
The `style` key is required. The value of the `style` key is one of the following strings: `'optional_positional'`, `'required_positional'`, `'optional_named'`, `'required_named'`.
|
||||
|
||||
The `defaultValue` key is required if the added parameter is an optional parameter whose type is non-nullable. The value of the `defaultValue` key is a [code template](#code-template) object whose value will be used when adding the parameter to any methods that override the element.
|
||||
|
||||
The `argumentValue` key is required if the added parameter is a required parameter or is an optional positional parameter that was added before at least one pre-existing optional positional parameter. The value of the `argumentValue` key is a [code template](#code-template) object whose value will be used as the value of the corresponding argument at invocation sites.
|
||||
|
||||
```yaml
|
||||
kind: 'addParameter'
|
||||
index: 0
|
||||
name: 'a'
|
||||
style: optional_positional
|
||||
argumentValue: ...
|
||||
```
|
||||
|
||||
#### addTypeParameter
|
||||
|
||||
An _addTypeParameter_ change indicates that an element was given a new type parameter. It has five keys: `kind`, `index`, `name`, `extends` and `argumentValue`.
|
||||
|
||||
The `index` key is required. The value of the `index` key is an integer indicating the index of the new type parameter after all the changes related to type parameters have been applied. It's used to know where to add new type parameters in overrides and where to add new type arguments in references to the element.
|
||||
|
||||
The `name` key is required. The value of the `name` key is a string containing the name of the type parameter. It's used as the name of new type parameters added in overrides.
|
||||
|
||||
The `extends` key is required if the type parameter has an extends clause. The value of the `extends` key is a [code template](#code-template) object whose value will be used in the `extends` clause of new type parameters added in overrides.
|
||||
|
||||
The `argumentValue` key is required. The value of the `argumentValue` key is a [code template](#code-template) object whose value will be used as the type argument when updating references to the element.
|
||||
|
||||
```yaml
|
||||
kind: 'addTypeParameter'
|
||||
index: 0
|
||||
name: 'T'
|
||||
extends: ...
|
||||
argumentValue: ...
|
||||
```
|
||||
|
||||
#### removeParameter
|
||||
|
||||
A _remove parameter_ change indicates that one of the parameters of a function or method was removed. It has three keys: `kind`, `index`, and `name`.
|
||||
|
||||
The `index` key is required unless the `name` key is provided, and isn't allowed if a `name` key is provided. The `index` key specifies that a positional parameter was removed and its value is the zero-based index of the parameter before the function was modified. It's used to know which parameters to remove in overrides and which arguments to remove at invocation sites.
|
||||
It's used to know:
|
||||
- where to remove the parameter in method overrides. If parameter `p` is to be removed from a method `m` in a class `A`, and the user has a subclass of `A` that overrides the method `m`, the tool will remove the parameter at `index` from the method `m` in the subclass.
|
||||
- if the argument is a positional parameter, which argument to remove at each invocation site.
|
||||
|
||||
For example, if the second positional parameter is removed you would write:
|
||||
|
||||
```yaml
|
||||
kind: 'removeParameter'
|
||||
index: 1
|
||||
```
|
||||
|
||||
The `name` key is required unless the `index` key is provided, and isn't allowed if an `index` key is provided. The `name` key specifies that a named parameter was removed and its value is the name of the parameter before the function was modified. It's used to know which parameters to remove in overrides and which arguments to remove at invocation sites.
|
||||
|
||||
For example, if the named parameter `p` is removed you would write:
|
||||
|
||||
```yaml
|
||||
kind: 'removeParameter'
|
||||
name: 'p'
|
||||
```
|
||||
|
||||
#### rename
|
||||
|
||||
A _rename_ change indicates that an element was renamed. It has two keys: `kind` and `newName`.
|
||||
|
||||
The `newName` key is required. The value of the `newName` key is a string containing the new name of the element. It's used to replace the old name of the element (provided in the element object) in references to the element.
|
||||
|
||||
For example, to rename an element to `B` you would write:
|
||||
|
||||
```yaml
|
||||
kind: 'rename'
|
||||
newName: 'B'
|
||||
```
|
||||
|
||||
This change is only intended to support simple renames, such as renaming a class or a method. It therefore assumes that the new element is like the old element in several ways:
|
||||
|
||||
- it’s in the same library (that is, the URI’s by which it’s imported don’t change),
|
||||
|
||||
- it’s the same kind of element (for example, you can’t rename a class and change it to be a mixin using this change), and
|
||||
|
||||
- if the element being renamed is a member (such as a method), then the renamed element is a member of the same container.
|
||||
|
||||
If the change that was made doesn’t fit within those constraints, then using a rename to capture it might not work in all situations. You should consider using a [replacedBy](#replacedBy) change instead.
|
||||
|
||||
#### renameParameter
|
||||
|
||||
A _renameParameter_ change indicates that a named parameter in a function or method was renamed. As such, the element in the transform must be a method or function. It has three keys: `kind`, `oldName`, and `newName`.
|
||||
|
||||
The `oldName` key is required. The value of the `oldName` key is a string containing the old name of the parameter. It's used to locate the parameter that was renamed.
|
||||
|
||||
The `newName` key is required. The value of the `newName` key is a string containing the new name of the parameter. It's used to replace the old name of the parameter, both in overrides of a method and in references to the method or function.
|
||||
|
||||
For example, to rename the parameter `a` to `b` you would write:
|
||||
|
||||
```yaml
|
||||
kind: 'renameParameter'
|
||||
oldName: 'a'
|
||||
newName: 'b'
|
||||
```
|
||||
|
||||
#### changeParameterType
|
||||
|
||||
A _changeParameterType_ change can be used to indicate that a parameter has been made non nullable. It has five keys: `kind`, `index`, `name`, `nullability` and `argumentValue`.
|
||||
|
||||
The `index` key is required unless the `name` key is provided, and isn't allowed if a `name` key is provided. The `index` key specifies that the position of the parameter that was changed.
|
||||
|
||||
The `name` key is required unless the `index` key is provided, and isn't allowed if an `index` key is provided. The `name` key specifies the name of the parameter that has the type change.
|
||||
|
||||
The `nullability` key is required and indicates the type of change made to the parameter. Currently `non_null` is the only type change that is supported.
|
||||
|
||||
The `argumentValue` key is optional and if provided will be used as the default value of the parameter. The value of the argumentValue key is a [code template](Data-driven-Fixes.md#code-template) object.
|
||||
|
||||
For eg, changing the parameter `a` to non null with a default value you would write:
|
||||
|
||||
```yaml
|
||||
kind: 'changeParameterType'
|
||||
name: 'a'
|
||||
nullability: 'non_null'
|
||||
argumentValue:
|
||||
expression: "'newValue'"
|
||||
```
|
||||
|
||||
|
||||
#### replacedBy
|
||||
|
||||
A _replacedBy_ change indicates that the specified element was replaced by another element. It has two keys: `kind` and `newElement`.
|
||||
|
||||
The `newElement` key is required. The value of the `newElement` key is an [element](#element) representing the element that replaces the specified element.
|
||||
|
||||
For example, to replace a top level variable `v` with a static field `C.f` you would write:
|
||||
|
||||
```yaml
|
||||
kind: 'replacedBy'
|
||||
newElement:
|
||||
uris: ['lib.dart']
|
||||
field: 'f'
|
||||
inClass: 'C'
|
||||
```
|
||||
|
||||
This change currently has two limitations that you should be aware of.
|
||||
|
||||
First, it doesn’t allow the list of URIs for the new element to be different from the list for the old element. That means that it currently might not correctly update the imports to make the new element visible in scope.
|
||||
|
||||
Second, it doesn’t support replacing every kind of element, nor can the replacement element be every possible kind. What it does support is the following cases:
|
||||
|
||||
- Replacing one constructor with a different constructor, even when the constructors are in different classes. It doesn’t correctly handle the case of replacing a `const` constructor with a non-`const` constructor.
|
||||
|
||||
- Replacing a function (either a top-level function or a static method) with a different function.
|
||||
|
||||
- Replacing a getter (either a top-level getter, a top-level variable, a static getter, or a static field) with a different getter.
|
||||
|
||||
- Replacing a setter (either a top-level setter, a top-level non-`final` variable, a static setter, or a non-`final` static field) with a different setter.
|
||||
|
||||
As with constructors, if a static member is being replaced by another static member, the members can be in different containers (classes, mixins, or extensions).
|
||||
|
||||
There isn’t currently any way for the tool to detect whether a method, getter, or setter is static, so it’s possible to specify an instance member. As a result, specifying an instance member (as either the replaced or replacing element) might produce invalid changes for your users.
|
||||
|
||||
Similarly, there isn’t currently any way for the tool to detect whether a top-level variable or a static field is `final` (which includes `const`). As a result, specifying a final element (as either the replaced or replacing element) might produce invalid changes for your users.
|
||||
|
||||
### code template
|
||||
|
||||
A _code template_ describes code that can be generated when fixing user code. It's represented as a map with three keys: `expression`, `requiredIf`, and `variables`.
|
||||
|
||||
The `expression` key is required. The value of the key is a template: a string containing Dart code, possibly with embedded references to variables. Variables are referenced by enclosing the name of the variable between `{%` and `%}` delimiters.
|
||||
|
||||
The `requiredIf` key is optional, but is only allowed if the code template is inside an `addParameter` change and the style of the parameter is `'optional_named'`. The value of the `requiredIf` key is a [condition](#condition) that will be evaluated for each invocation of the method or function that was changed. Normally arguments are not added for optional named parameters, but the argument _will_ be added if the `requiredIf` key is provided and the condition evaluates to `true`.
|
||||
|
||||
The `variables` key is optional. The value of the `variables` key is a [variable map](#variable-map). If neither the `expression` template nor the `requiredIf` condition use any variables then the `variables` key can be omitted.
|
||||
|
||||
Variables serve two purposes. The first, and most obvious, is to allow the replacement text to contain values based on the context in which it will appear. For example, if a parameter was removed then it’s possible that the value of the corresponding argument will need to appear somewhere else in the updated code.
|
||||
|
||||
The second purpose is to allow the tool to automate some of the changes for you. For example, if the replacement text needs to refer to an imported name, you should use a variable to do so, and the definition of the variable can specify where the name is imported from. The tool can then determine whether any changes need to be made to the imports in the library being edited and make them correctly.
|
||||
|
||||
The variables that can be referenced in the template are the variables defined in the code template and any variables defined in the enclosing transform whose name isn't hidden by a variable defined in the code template.
|
||||
|
||||
```yaml
|
||||
expression: '{% type %}({% arg %})'
|
||||
variables:
|
||||
arg: ...
|
||||
type: ...
|
||||
```
|
||||
|
||||
### variable map
|
||||
|
||||
A _variable map_ defines one or more variables. It's represented as a map. The keys of the map are variable names and the values are [value](#value) objects.
|
||||
|
||||
The name of a variable is any identifier containing either uppercase or lowercase letters.
|
||||
|
||||
### value
|
||||
|
||||
A _value_ describes a way of computing a string that can be injected into a template. It's represented as a map. The number and names of the keys depends on the kind of value being described, but all value objects have a `kind` key.
|
||||
|
||||
The `kind` key is required. The value of the `kind` key is a string indicating the kind of value. The valid kinds are:
|
||||
- [fragment](#fragment)
|
||||
- [import](#import)
|
||||
|
||||
Individual kinds of value objects are described in the sections below, titled by the kind.
|
||||
|
||||
#### fragment
|
||||
|
||||
A _fragment_ value indicates that the value is a fragment copied from the code being updated. It has two keys: `kind` and `value`.
|
||||
|
||||
The `value` key is required. The `value` key specifies the fragment of code to be copied and is expressed as a chain of references in a simplified AST model of the code. The details follow the example.
|
||||
|
||||
```yaml
|
||||
kind: fragment
|
||||
value: 'arguments[0]'
|
||||
```
|
||||
|
||||
The code fragment is described by a dot separated non-empty list of accessors:
|
||||
|
||||
```
|
||||
<fragment> ::= <accessor> ('.' <accessor>)*
|
||||
```
|
||||
|
||||
Each accessor consists of an identifier followed by an index operator:
|
||||
|
||||
```
|
||||
<accessor> ::= <identifier> '[' (<integer> | <identifier>) ']'
|
||||
```
|
||||
|
||||
When an accessor is evaluated it takes a target node and either produces a result node or fails. The target node of the first accessor is always the node representing the invocation being updated (whether it’s the invocation of a function, method, getter, setter or constructor). The target node of all other accessors is the result node of the preceding accessor.
|
||||
|
||||
There are currently two supported identifiers: `arguments` and `typeArguments`.
|
||||
|
||||
The `arguments` accessor takes any node that includes an argument list and returns one of the arguments to the invocation. The index operator selects the argument to be returned. A zero-based integer is used to select a positional argument and an identifier is used to select a named argument.
|
||||
|
||||
The `typeArguments` accessor takes any node that includes a type argument list and returns one of the type arguments. The index operator selects the argument to be returned using a zero-based integer.
|
||||
|
||||
Here are some examples:
|
||||
- `arguments[0]` copies the code for the first argument in the argument list.
|
||||
- `arguments[child]` copies the code for the argument named `child`.
|
||||
- `arguments[0].typeArguments[0]` copies the code for the first type argument in the expression that is the first argument in the argument list.
|
||||
|
||||
#### import
|
||||
|
||||
An _import_ value indicates that the value is a top-level declaration from a library. It has three keys: `kind`, `uris` and `name`.
|
||||
|
||||
The `uris` key is required. The value of the `uris` key is a list of [uri](#uri)s, each of which is the URI of a public library from which the name can be imported. They will be used to add an `import` directive, if one is needed, to any library in which the value of the code template will be used. If there are multiple URIs in the list and none of them are imported into the library being modified, then the first URI will be used.
|
||||
|
||||
The `name` key is required. The `name` key specifies the name of the top-level declaration and its value is a string containing that name. The name is also the value of the variable. Any use of the variable in the template will ensure that the library is imported into the library being changed and that the name is visible.
|
||||
|
||||
```yaml
|
||||
kind: import
|
||||
uris: [ 'package:flutter/material.dart' ]
|
||||
name: 'Widget'
|
||||
```
|
||||
|
||||
### uri
|
||||
|
||||
A _uri_ is a string containing a URI. The URI can be one of the following:
|
||||
- a `dart:` URI,
|
||||
- a `package:` URI, or
|
||||
- an abbreviated URI.
|
||||
|
||||
An abbreviated URI is the portion of the URI following the name of the package containing the data file. For example, if a package named `sample` contains a library named `sample.dart` in the root of the `lib` directory, then within the data file for the package `sample` the URI for that library can be written as either `package:sample/sample.dart` or `sample.dart`.
|
||||
|
||||
### test folder
|
||||
|
||||
A _test folder_ contains dart files and their curresponding golden master `.expect` files that are used to test the data in the [data file](#data-file). These files are located in a folder (conventionally named `test_fixes`) in the package directory. See the [Testing](#testing) section for more documentation.
|
||||
|
||||
You might find it useful to include a `README.md` file that has a link to this documentation for easy reference.
|
||||
|
||||
|
||||
## Testing
|
||||
|
||||
This section provides a guide for testing data driven fixes.
|
||||
|
||||
All the test files are contained in the [test folder](#test-folder). Every dart file has a golden counterpart file, which is named as `<dart-file-name>.dart.expect`.
|
||||
|
||||
Every test has two parts:
|
||||
1. All possible usages of the public API that is changed by the data driven fix. This is contained in the dart file.
|
||||
2. All the usages in the dart file with the expected change applied. This is contained in the golden file.
|
||||
|
||||
Extending on the example shown in [Rename a method](#rename-a-method), the change can be tested as below:
|
||||
```dart
|
||||
// test_fixes/C.dart
|
||||
import 'package:<package-name>/C.dart';
|
||||
|
||||
C.oldName('Fix me'); // usage before the change.
|
||||
```
|
||||
|
||||
```dart
|
||||
// test_fixes/C.dart.expect
|
||||
import 'package:<package-name>/C.dart';
|
||||
|
||||
C.newName('Fix me'); // expected usage after the change.
|
||||
```
|
||||
|
||||
To run these tests locally, execute the below command in the [test folder](#test-folder).
|
||||
```bash
|
||||
dart fix --compare-to-golden
|
||||
```
|
||||
65
docs/contributing/Design-Documents.md
Normal file
65
docs/contributing/Design-Documents.md
Normal file
@@ -0,0 +1,65 @@
|
||||
If you want to write a design doc for people to review, we recommend using Google Docs.
|
||||
We have a template you can use, at [flutter.dev/go/template](https://flutter.dev/go/template). It describes the process for minting a `flutter.dev/go/foo` shortlink for your design doc.
|
||||
We recommend you use that template so that people can immediately recognize that this is a Flutter design document and that it is shared publicly.
|
||||
|
||||
Don't forget to configure your document's Sharing settings so that everyone has comment access. The idea of sharing the document in this way is not necessarily to proactively obtain feedback from the entire community; it is to make it _possible_ for people to share the document with anyone in the community, whether they work for your employer or not, and whether you have personally shared the document with them yet or not.
|
||||
|
||||
The template discusses how to create a shortlink for your design doc (flutter.dev/go/...). When creating the shortlink, remember to test the URL you are publishing in an incognito window!
|
||||
|
||||
Googlers: Design docs must be created by non-corp accounts! See [Contributor Access](Contributor-access.md#fcontriborg-accounts) for details on getting `fcontrib.org` accounts if you don't want to use your personal GMail account.
|
||||
|
||||
When you implement a design, document it in the source code in detail. The API documentation is the usual place where we document our designs. It's perfectly reasonable for API docs to be multiple pages long with subheadings (e.g. see the docs for [RenderBox](https://master-api.flutter.dev/flutter/rendering/RenderBox-class.html)!). Do not assume that anyone will ever read your design doc after the discussion has finished. Similarly, do not assume that anyone will look at closed GitHub issues or PR discussions.
|
||||
|
||||
_See also: [list of current design docs](https://github.com/flutter/flutter/issues?q=is%3Aopen+is%3Aissue+label%3A%22design+doc%22)_
|
||||
|
||||
## Purpose of design docs
|
||||
|
||||
The Flutter project uses design docs as a tool for guiding discussions.
|
||||
|
||||
Decisions are made in PRs, not in design docs.
|
||||
|
||||
Approvals are given in PRs, not in design docs.
|
||||
|
||||
## Soliciting feedback
|
||||
|
||||
If you wish to get feedback on your design doc, you have many options for doing so, depending on how much feedback you want:
|
||||
|
||||
* If there is an issue already filed on the topic, definitely put a link to the design doc there. People who have found the issue and want to get updates on the topic will have subscribed to the issue, so this is the most effective way to communicate with them.
|
||||
|
||||
* File a new issue to track the design doc using [the design doc issue template](https://github.com/flutter/flutter/issues/new?template=8_design_doc.yml). Assign it to yourself.
|
||||
|
||||
* Post the link on Discord. You can post it to #hidden-chat to just get feedback from team members. You can post it to one or more of the #hackers-* channels if you want feedback from people who are interested in the general area. You can post it to the global #hackers channel if you want feedback from anyone interested in working on Flutter. If you really want feedback, you can post a request to #announcements and publish it to any server that is following ours.
|
||||
|
||||
* If you want feedback from the broad community, tweet out the link and let other team members know so that we can retweet it. Similarly, you can post the request to one of the Flutter reddit channels, such as r/FlutterDev.
|
||||
|
||||
* You can ask our developer relations (devrel) team to broadcast a request for comments. (Start by asking in #hackers-devrel; if nobody responds, ping Hixie on that channel.)
|
||||
|
||||
* You can ask our user experience researcher (UXR) team to study the proposal and potentially test it with real users, or collect relevant data from the next quarterly survey. (Start by asking in #hackers-devexp; if nobody responds, ping Hixie on that channel.)
|
||||
|
||||
* If you have commit access, you can ask to talk about the design doc at the next Dash Forum meeting (normally held on Tuesdays at 11am US west coast time). Ping Hixie on #hidden-chat to get on the schedule, or use the form to request to be added, the link for which is pinned in the #hidden-chat channel.
|
||||
|
||||
### How to get good feedback
|
||||
|
||||
Often, you will solicit feedback, and get none. There are many causes of this.
|
||||
|
||||
Maybe your proposal is unclear, and so people don't really know what to suggest. People are often reluctant to provide broad criticisms. Consider if you can improve the clarity of your design doc. Do you have a clear problem statement separate from your solution? Do you show example code of the problem? Do you have screenshots or diagrams of the problem? For your solution, do you start from first principles and explain it? Often it's easy to forget that your readers don't have the same context you do, so without a gentle introduction they'll get lost very quickly. Do you have sample code of your proposed solution(s)? Do you need more diagrams or screenshots? Ask someone you trust if they think your document is sufficiently clear.
|
||||
|
||||
Maybe your proposal is too big for anyone to get their head around. Can it be split into smaller components, so that each one can be understood separately, before bringing all the pieces together into your grand design? (You can do this all in the same doc.)
|
||||
|
||||
Maybe people don't know what to provide feedback about. If you have an area you are particularly interested in getting feedback about, it can be very helpful to explicitly invite such feedback.
|
||||
|
||||
Maybe you are asking the wrong people. Consider the suggestions in the earlier section, and reach out explicitly to people who are affected by your proposal. Consider escalating, asking more and more people until you get the volume of feedback you desire.
|
||||
|
||||
Maybe everyone agrees. Consider leaving some intentionally sketchy details in your proposal to encourage people to engage! (This is a risky strategy, sometimes people end up _liking_ your "bad" ideas...)
|
||||
|
||||
Maybe your proposal is too obvious or uninteresting. Sometimes, a change is so uncontroversial and simple that frankly it would be better just to write the PR and submit it.
|
||||
|
||||
## Content in design docs
|
||||
|
||||
### Screen captures
|
||||
|
||||
The easiest way to capture videos for design docs is using macOS. Press Command+Shift+5 for a whole bunch of options.
|
||||
|
||||
### Diagrams
|
||||
|
||||
As we use Google Docs for the text portion of design docs, the easiest way to draw diagrams is using Google Diagrams. Select `Insert` > `Drawing` > `New` to create a new diagram.
|
||||
@@ -13,7 +13,7 @@
|
||||
Verify that `adb` is in your [PATH](https://en.wikipedia.org/wiki/PATH_(variable)) (that `which adb` prints sensible output).
|
||||
|
||||
If you're
|
||||
[also working on the Flutter engine](https://github.com/flutter/flutter/wiki/Setting-up-the-Engine-development-environment),
|
||||
[also working on the Flutter engine](../engine/dev/Setting-up-the-Engine-development-environment.md),
|
||||
you can use the copy of the Android platform tools in
|
||||
`.../engine/src/third_party/android_tools/sdk/platform-tools`.
|
||||
|
||||
@@ -59,6 +59,6 @@ Next steps:
|
||||
|
||||
* [Running examples](https://github.com/flutter/flutter/wiki/Running-examples), to see if your setup works.
|
||||
* [The flutter tool](https://github.com/flutter/flutter/wiki/The-flutter-tool), to learn about how the `flutter` command line tool works.
|
||||
* [Style guide for Flutter repo](https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo), to learn how to write code for Flutter.
|
||||
* [Tree hygiene](https://github.com/flutter/flutter/wiki/Tree-hygiene), to learn about how to submit patches.
|
||||
* [Signing commits](https://github.com/flutter/flutter/wiki/Signing-commits), to configure your environment to securely sign your commits.
|
||||
* [Style guide for Flutter repo](Style-guide-for-Flutter-repo.md), to learn how to write code for Flutter.
|
||||
* [Tree hygiene](Tree-hygiene.md), to learn about how to submit patches.
|
||||
* [Signing commits](Signing-commits.md), to configure your environment to securely sign your commits.
|
||||
|
||||
48
docs/contributing/Signing-commits.md
Normal file
48
docs/contributing/Signing-commits.md
Normal file
@@ -0,0 +1,48 @@
|
||||
Flutter repositories require that your commits are signed (as of Nov 17, 2021, this is not enabled in all repos, but it's best to be prepared). To get started follow the GitHub instructions in [Signing Commits][1].
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### `GIT_TRACE=1`
|
||||
|
||||
If a `git` command fails with a complaint about `gpg`, run the same command again with `GIT_TRACE=1`. For example, if the failing command is:
|
||||
|
||||
```
|
||||
git commit -S -m 'some message'
|
||||
```
|
||||
|
||||
Run this instead:
|
||||
|
||||
```
|
||||
GIT_TRACE=1 git commit -S -m 'some message'
|
||||
```
|
||||
|
||||
With tracing enabled `git` will provide more information about the failure.
|
||||
|
||||
### fatal: failed to write commit object
|
||||
|
||||
If you see the following output from `git commit -S`:
|
||||
|
||||
```
|
||||
error: gpg failed to sign the data
|
||||
fatal: failed to write commit object
|
||||
```
|
||||
|
||||
This error may indicate that your GPG name, comment, and email do not match those of `git`. To fix this issue, add `user.signingkey` to your git configuration referring to the GPG key by its hash rather than by name. To find out the GPG key hash run:
|
||||
|
||||
```
|
||||
gpg --list-secret-keys --keyid-format=long
|
||||
```
|
||||
|
||||
In the list of keys printed to the console find the one you want to use (the one you registered with GitHub) and copy its hash from the line that begins with `sec`. For example, the key hash in the following line is `XYZ`:
|
||||
|
||||
```
|
||||
sec rsa4096/XYZ 20201-11-16
|
||||
```
|
||||
|
||||
To add it to your git configuration run:
|
||||
|
||||
```
|
||||
git config --global user.signingkey XYZ
|
||||
```
|
||||
|
||||
[1]: https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits
|
||||
1915
docs/contributing/Style-guide-for-Flutter-repo.md
Normal file
1915
docs/contributing/Style-guide-for-Flutter-repo.md
Normal file
File diff suppressed because it is too large
Load Diff
481
docs/contributing/Tree-hygiene.md
Normal file
481
docs/contributing/Tree-hygiene.md
Normal file
@@ -0,0 +1,481 @@
|
||||
## tl;dr
|
||||
|
||||
- Regressions should be [reverted first](https://github.com/flutter/flutter/wiki/Landing-Changes-With-Autosubmit) and questions asked later. Bringing the tree to green is higher priority.
|
||||
- A breaking change is one that breaks the tests in the flutter/tests repo, and those need a migration guide.
|
||||
- Expect that a new patch will be reviewed within two weeks, unless it is fixing a P0 bug in which case it should be reviewed the same day. If it has not been reviewed in that timeframe, reach out on [Chat](Chat.md). Remember that reviewers are human beings with additional professional and personal responsibilities.
|
||||
|
||||
## Introduction
|
||||
|
||||
This page covers how to land a PR and other aspects of writing code for
|
||||
Flutter other than the actual writing of the code. For guidance on
|
||||
designing APIs, documenting, and formatting your code, see the
|
||||
[Style guide for Flutter repo](Style-guide-for-Flutter-repo.md) document.
|
||||
|
||||
## Overview
|
||||
|
||||
The general process for submitting code to a Flutter repository is as follows:
|
||||
|
||||
1. Fork the repository on GitHub (see the
|
||||
[contributing guide](https://github.com/flutter/flutter/blob/main/CONTRIBUTING.md)
|
||||
for advice on doing this and in general setting up your development environment).
|
||||
|
||||
2. If there is not already an issue covering the work you are interested in doing,
|
||||
then file a new bug to describe the issue you are addressing. Having an issue means
|
||||
that if we have to revert the PR, we can reopen the issue and not lose track of the
|
||||
fact that the work didn't fully land. Similarly if someone works on a PR then stops
|
||||
before it can land, having an issue means we have somewhere to point to the code when
|
||||
we close the PR without landing it, so other people can take it over.
|
||||
|
||||
3. Discuss your design on the issue. See [Design Documents](Design-Documents.md) for advice.
|
||||
You may find it useful to create a Google Doc to
|
||||
solicit feedback (use the template at [flutter.dev/go/template](https://flutter.dev/go/template)).
|
||||
You may wish to e-mail the mailing list, or discuss the topic
|
||||
on our [Chat](Chat.md) channels. The more buy-in you get from the rest of the
|
||||
team (especially the relevant leads), the easier the rest of the process will be.
|
||||
You can put the label "proposal" on your issue to indicate that you have a design
|
||||
up for discussion in the issue.
|
||||
|
||||
4. If the work you are doing affects our privacy surface, such as modifying how
|
||||
we collect analytics, crash logs, or the like, then please reach out to a Googler
|
||||
to discuss your changes (you'll want to start a Google Doc to
|
||||
solicit feedback, use the template at [flutter.dev/go/template](https://flutter.dev/go/template)),
|
||||
who will be happy to loop in one of our engineers who explicitly focus on privacy issues
|
||||
so that they're able to give feedback on the work you plan to do.
|
||||
|
||||
5. Create a branch off of `main` (or if the repository has not yet switched to
|
||||
having a `main` branch, from `master`) on your GitHub fork of the repository, and implement
|
||||
your change. Make sure it is tested (see the next section for details).
|
||||
|
||||
You must follow the guidelines described in the [Style guide for Flutter repo](Style-guide-for-Flutter-repo.md).
|
||||
Files must not have trailing spaces.
|
||||
For the engine repository, C, C++, and Objective-C code should be formatted with
|
||||
`clang-format` before submission (use `buildtools/<OS>/clang/bin/clang-format
|
||||
--style=file -i`).
|
||||
|
||||
6. Submit this branch as a PR to the relevant Flutter repository.
|
||||
_(See also: [Signing commits](./Signing-commits.md))_
|
||||
|
||||
7. Get your code reviewed (see below). You should probably reach out to the relevant
|
||||
expert(s) for the areas you touched and ask them to review your PR directly.
|
||||
GitHub sometimes recommends specific reviewers; if you're not sure who to ask,
|
||||
that's probably a good place to start.
|
||||
|
||||
8. Make sure your PR passes all the pre-commit tests. Consider running some of the
|
||||
post-commit tests locally (see the
|
||||
[devicelab](https://github.com/flutter/flutter/blob/main/dev/devicelab/README.md)
|
||||
directory). If any tests break, especially the `customer_testing` tests, please
|
||||
see the breaking change policy section below for details on how to proceed.
|
||||
|
||||
The `luci-flutter` test isn't checking your PR, it's letting you know
|
||||
whether the tree itself is passing the tests right now (including post-
|
||||
commit tests).
|
||||
|
||||
**If the trees or dashboards are showing any regressions, only fixes
|
||||
that improve the situation are allowed to go in.**
|
||||
|
||||
9. Once everything is green and you have an LGTM from the owners of the code you are affecting (or someone to whom they
|
||||
have delegated), and an LGTM from any other contributor who left comments, add the "autosubmit" label if you're in the flutter-hackers github group. A bot will land the patch when it feels like it. If you're not in the flutter-hackers group a reviewer will add the label for you.
|
||||
|
||||
10. Watch the post-commit tests on the [dashboard](https://dashboard.flutter.dev/#/build) to make sure everything passes. If anything
|
||||
goes wrong, revert your patch and study the problem. You should aim to be the one to revert your patch. You will be racing everyone
|
||||
else on the team who will also be trying to revert your patch. (See below for guidance on reverting PRs.)
|
||||
|
||||
_See also: [What should I work on?](What-should-I-work-on.md)_
|
||||
|
||||
## Tests
|
||||
|
||||
Every change in the flutter/engine, flutter/flutter, and flutter/packages repos must be tested, except for PRs that:
|
||||
|
||||
* only remove code (no modified or added lines) to remove a feature or remove dead code. (Removing code to fix a bug still needs a test.)
|
||||
* only affect comments (including documentation).
|
||||
* only affect code inside the `.github` directory, `.cirrus.yml` or `.ci.yaml` config files.
|
||||
* only affect `.md` files.
|
||||
* are generated by automated bots (rollers).
|
||||
* have an _explicit_ exemption (a message from a member of `@test-exemption-reviewer` on Discord starting with `test-exempt:`).
|
||||
<!-- update https://github.com/flutter/cocoon/blob/main/app_dart/lib/src/service/config.dart if you change who is on the list -->
|
||||
|
||||
**In the repositories listed above, a bot will comment on your PR if you need an explicit exemption.** Do not land a PR that has had such a message from the bot unless it has an explicit exemption! The message tells you how to ask for an exemption. Please follow those instructions.
|
||||
|
||||
**If the PR is in a repository that is not listed above, meaning is not supported by the bot, then it is the responsibility of the pull request reviewer to make sure that tests have been added to support the code change.**
|
||||
|
||||
In particular, the following kinds of PRs are *not* automatically exempt and require an explicit comment even though the answer may be obvious: refactors with no semantic change (e.g. null safety migrations), configuration changes in the aforementioned repos, changes to analysis (fixing lints, turning on lints), changes to test infrastructure, manual dependency rolls, fixes to existing tests, cosmetic fixes to unpublished example apps.
|
||||
|
||||
If a reviewer says a PR should have a test, then it needs a test regardless of the exemptions above.
|
||||
|
||||
PRs adding data-driven fixes require tests that fall under the test_fixes directory, but are not yet recognized by the bot as being tested.
|
||||
|
||||
Consider using the code coverage tools to check that all your new code is covered by tests (see [Test coverage for package:flutter](./testing/Test-coverage-for-package-flutter.md)).
|
||||
|
||||
Feel free to update the bot's logic to catch more cases that should be automatically exempt (it's in the cocoon repo).
|
||||
|
||||
## Using git
|
||||
|
||||
Assuming your environment has been configured according to the instructions in
|
||||
[Setting up the Engine development environment](../engine/dev/Setting-up-the-Engine-development-environment.md),
|
||||
[Setting up the Framework development environment](Setting-up-the-Framework-development-environment.md), or
|
||||
[Setting up the Packages development environment](../ecosystem/contributing/Setting-up-the-Packages-development-environment.md),
|
||||
follow these steps to start working on a patch:
|
||||
|
||||
* `git fetch upstream`
|
||||
* `git checkout upstream/main -b name_of_your_branch`
|
||||
* `flutter update-packages`
|
||||
* Hack away.
|
||||
* `git commit -a -m "<your informative commit message>"`
|
||||
* `git push origin name_of_your_branch`
|
||||
|
||||
GitHub provides you with a link for submitting the pull request in the message output by `git push`.
|
||||
|
||||
Because `git pull` will often miss tags that are used to define the release of the flutter tool, it is recommended to use `git fetch` typically to avoid version mismatches when running `flutter update-packages`.
|
||||
|
||||
Use `git fetch upstream; git rebase upstream/main; git push origin your_branch_name` to update your PRs, rather than using merge, because that way our tooling will recognize your PR as being up to date. (Otherwise it'll try testing against the tests at the time you originally branched.) Also, **be wary of force pushing to your PR branch** if you are dealing with golden image tests; see [gold troubleshooting instructions](./testing/Writing-a-golden-file-test-for-package-flutter.md#troubleshooting).
|
||||
|
||||
Please make sure all your patches have detailed commit messages explaining what the problem was and
|
||||
what the solution is.
|
||||
|
||||
You must complete the
|
||||
[Contributor License Agreement](https://cla.developers.google.com/clas).
|
||||
You can do this online, and it only takes a minute.
|
||||
|
||||
|
||||
## Getting a code review
|
||||
|
||||
Every PR must be code-reviewed before check-in, including things like
|
||||
rolling a dependency. Getting a review means that a regular Flutter
|
||||
contributor (someone with commit access; see [contributor access](Contributor-access.md) for details) has "approved" the PR in the
|
||||
GitHub UI. We call this "getting an LGTM" ("looks good to me").
|
||||
|
||||
If you are not yourself someone with commit access, then a second person
|
||||
with commit access must also review and approve your PR. This ensures that
|
||||
two people with commit access (trusted contributors) agree on every commit.
|
||||
|
||||
### Why
|
||||
|
||||
Code review serves many critical purposes. There's the obvious
|
||||
purpose: catching errors. Even the most experienced engineers
|
||||
frequently make errors that are caught by code review. But there are
|
||||
also many other benefits of code reviews:
|
||||
|
||||
* It spreads knowledge among the team. Since every line of code will
|
||||
have been read by two people, it's more likely that once you move
|
||||
on, someone else will understand the code.
|
||||
|
||||
* It keeps you honest. Knowing that someone will be reading your
|
||||
code, you are less tempted to cut corners and more motivated to
|
||||
write code you are proud of.
|
||||
|
||||
* It exposes you to different modes of thinking. Your code reviewer
|
||||
has probably not thought about the problem in the same way you
|
||||
have, and so may have a fresh perspective and may find you a better
|
||||
way to solve the problem.
|
||||
|
||||
We recommend you consider
|
||||
[these suggestions](https://testing.googleblog.com/2017/06/code-health-too-many-comments-on-your.html)
|
||||
for addressing code review comments on your PR.
|
||||
|
||||
### When
|
||||
|
||||
If you're working on a big patch, don't hesitate to get reviews early,
|
||||
before you're ready to check code in. Also, don't hesitate to ask for
|
||||
multiple people to review your code, and don't hesitate to provide unsolicited
|
||||
comments on other people's PRs (although approvals in the GitHub UI
|
||||
should be reserved for those with contributor access). The more
|
||||
reviews the better.
|
||||
|
||||
If nobody reviews your PR within two weeks, you can ask for
|
||||
a review via our [Chat](Chat.md) channels. Start by asking in #hackers,
|
||||
saying what your patch does and providing a link.
|
||||
|
||||
### Who
|
||||
|
||||
PRs are assigned reviewers weekly. The precise process varies by team but tends to be combined with issue [triage](https://github.com/flutter/flutter/wiki/Triage).
|
||||
|
||||
Code should be reviewed by the owner (tech lead) of the area(s) of the codebase that you are changing,
|
||||
or someone to whom they have delegated that authority.
|
||||
If anyone else leaves comments, please also wait for their approval (LGTM) before landing code.
|
||||
|
||||
If nobody has reviewed your code after a week, then reach out on our [Chat](Chat.md) channels.
|
||||
The `#hackers-new` channel is a good place to ask for help if you're a new contributor.
|
||||
|
||||
_For PRs affecting the `material` and `cupertino` libraries, team members are expected to seek reviewers directly;
|
||||
they will not be assigned during PR triage._
|
||||
|
||||
### How
|
||||
|
||||
Code review status is managed via GitHub's approval system. PRs should
|
||||
not be merged unless one or more contributors with commit access (at
|
||||
least one of which should be very familiar with the code in question)
|
||||
have approved the PR in the GitHub UI.
|
||||
|
||||
Reviewers should carefully read the code and make sure they understand
|
||||
it. A reviewer should check the code for both high level concerns,
|
||||
such as whether the approach is reasonable and whether the code's structure makes sense, as well as
|
||||
lower-level issues like how readable the code is and adherence to the
|
||||
[Flutter style guide](Style-guide-for-Flutter-repo.md).
|
||||
Use [these best practices](https://mtlynch.io/human-code-reviews-1/)
|
||||
when reviewing code and providing comments.
|
||||
|
||||
As a reviewer, you are the last line of defense.
|
||||
|
||||
0. Did the author sign the CLA? If not, ask them to do so and don't look at the code.
|
||||
1. Take a step back. What problem is the PR trying to solve? Is it a real problem?
|
||||
2. What other solutions could we consider? What could we do to make this even better?
|
||||
3. Is it the best API? See our [philosophy](Style-guide-for-Flutter-repo.md#philosophy) section. Look for state duplication, synchronous slow work, complecting, global state, overly-specific APIs, API cliffs and API oceans, API design in a vacuum (without a customer). If these terms don't make sense, read the style guide again. :-)
|
||||
4. Is it the best implementation? Again, see our [style guide](Style-guide-for-Flutter-repo.md#coding-patterns-and-catching-bugs-early), in particular its section on good coding patterns. Are there hacks? Are we taking on more technical debt? Think of ways in which the code could break.
|
||||
5. Is it testable? Is it tested? **All code must be tested.** Are there asserts? Encourage liberal use of assertions.
|
||||
6. Look for mistakes in indenting the code and other trivial formatting problems.
|
||||
7. Is new code licensed correctly?
|
||||
8. Is the documentation thorough and useful? Look for useless documentation, empty prose, and breadcrumbs. See the [documentation section](hStyle-guide-for-Flutter-repo.md#documentation-dartdocs-javadocs-etc) of our style guide for what that means.
|
||||
9. Check for good grammar in API docs and comments. Check that identifiers are named according to our conventions.
|
||||
|
||||
Once you are satisfied with the contribution, and _only_ once you are satisfied,
|
||||
use the GitHub "Approval" mechanism (an "LGTM" comment is not sufficient).
|
||||
If you feel like you are being worn down, hand the review to someone else. Consider
|
||||
our [conflict resolution](https://github.com/flutter/flutter/blob/main/CODE_OF_CONDUCT.md#conflict-resolution)
|
||||
policy if you feel like you are being forced to agree to something you don't like.
|
||||
|
||||
Reviewers should not give an LGTM unless the patch has tests that verify
|
||||
all the affected code, or unless a test would make no sense. If you
|
||||
review a patch, you are sharing the responsibility for the patch with
|
||||
its author. You should only give an LGTM if you would feel confident
|
||||
answering questions about the code.
|
||||
|
||||
In general, reviewers should favor approving a PR once it is in a state where it definitely improves the overall code health of the system being worked on, even if the PR isn't perfect. Reviewers should also provide positive feedback throughout the process, to offset the impact of the stream of critiques that is code review.
|
||||
|
||||
Reviewers should always feel free to leave comments expressing that something could be better, but if it's not very important, prefix it with something like "Shouldn't block this PR but: " to let the author know that it's just a point of polish that they could choose to ignore in the current PR (these should be documented in TODO comments with a tracking issue).
|
||||
|
||||
If you are not a regular Flutter contributor (someone with commit access),
|
||||
we very much welcome your reviews on code contributions in the form of comments
|
||||
on the code, but please refrain from approving or LGTM'ing changes, as it
|
||||
confuses PR authors, who may think your approval is authoritative and merge
|
||||
the PR prematurely.
|
||||
|
||||
When commenting on a PR, keep in mind the following mantra:
|
||||
|
||||
* Be polite and grateful. Graceful professionalism.
|
||||
* Explain what is happening. Explain why it is happening.
|
||||
* Provide next steps. Set expectations.
|
||||
|
||||
It's better to close a PR than to leave it in limbo.
|
||||
|
||||
_See also: [How to review a Flutter PR](https://docs.google.com/presentation/d/1apKVLEAEqxINby49JhLWSLI-CMH0nxCcnrf90nW4cts/edit?usp=sharing) presentation_
|
||||
|
||||
### What (to do when the patch is abandoned)
|
||||
|
||||
Sometimes the contributor is unable to finish the work of landing the patch. In that case, if the PR has promise, we may close it but mention it on the relevant issue so that other interested parties can pick it up. Such issues are given the label [`has partial patch`](https://github.com/flutter/flutter/labels/has%20partial%20patch).
|
||||
|
||||
## Landing a patch
|
||||
|
||||
Once you have submitted your patch and received your LGTM, if you do not have commit access to
|
||||
the repository yet, then wait for one of the project maintainers to submit it for you.
|
||||
|
||||
If you do have access, add the "autosubmit" label. A bot will land the PR for you.
|
||||
|
||||
|
||||
### Squashing commits
|
||||
|
||||
When you squash commits, by default, GitHub will concatenate all your commit messages to form a unified commit message. This often yields an overly verbose commit message with many unhelpful entries (e.g. "fix typo"). Please double-check (and hand-edit if necessary) your commit message before merging such that the message contains a helpful description of the overall change.
|
||||
|
||||
|
||||
## Tree breakage (regressions in functionality)
|
||||
|
||||
If a check-in has caused a regression on the main branch (sometimes "master") for any of the flutter repositories,
|
||||
revert (roll back) the check-in (even if it isn't yours). Do not attempt to forward-fix post-submit test failures.
|
||||
|
||||
There is no shame in making mistakes! Reverts happen all the time and are a normal part of engineering.
|
||||
|
||||
To revert a PR, just add the `revert` label to it. _For more details, see [Landing Changes With Autosubmit](https://github.com/flutter/flutter/wiki/Landing-Changes-With-Autosubmit)._
|
||||
|
||||
|
||||
### Avoid "Revert "Revert "Revert "Revert "Fix foo"""" commit messages
|
||||
|
||||
Please limit yourself to one "Revert" per commit message, otherwise we won't
|
||||
have any idea what is actually landing. Is it putting us back to where we were before?
|
||||
Is it adding new code? Is it a controversial new feature that actually caused
|
||||
a regression before but is now fixed (we hope)?
|
||||
|
||||
Only use "Revert" if you are actually returning us to a known-good state.
|
||||
|
||||
Also avoid using "Reland" in the commit message.
|
||||
When you later revert the revert, just land the PR afresh with the original commit message,
|
||||
possibly updated with the information since collected, and include a link
|
||||
to the original PR and to the revert PR so that people can follow the breadcrumbs later.
|
||||
|
||||
|
||||
## Regressions in performance
|
||||
|
||||
After each check-in, please monitor the [performance dashboards](https://flutter-dashboard.appspot.com/).
|
||||
|
||||
If you see a regression (any of the charts increasing after your commit), please
|
||||
follow these steps:
|
||||
|
||||
* Comment on the PR acknowledging the regression.
|
||||
* If the regression is expected and is a desirable trade-off (e.g. disk size
|
||||
increased slightly in exchange for a significant improvement in speed), then
|
||||
rebaseline the relevant benchmarks (log in, then click the magnifying glass
|
||||
at the top right of each chart, then click the button to auto rebaseline and
|
||||
commit).
|
||||
* If the regression is not expected, and may be a problem in your PR, revert
|
||||
your PR and investigate.
|
||||
* If the regression is not expected, and is quite severe, revert your PR and
|
||||
investigate.
|
||||
* If the regression is not expected, and is not severe, and is definitely not
|
||||
a problem in your PR (e.g. you changed a comment and the analyzer performance
|
||||
got worse, or you deleted a README and the rasterizer slowed down), then file
|
||||
a bug, labeled with the "regression", "performance", `P0` labels, and either
|
||||
investigate or delegate to someone to investigate. The investigation should be
|
||||
considered a high priority. It is your responsibility to make sure that the
|
||||
cause is understood within a few days.
|
||||
|
||||
Performance regressions are not a problem so long as they are promptly dealt with.
|
||||
Therefore, Flutter considers all unexpected performance regressions to be `P0` until
|
||||
we have it under control (e.g. we know what caused it and either have a fix under
|
||||
way or have determined it is an acceptable trade-off).
|
||||
|
||||
### Performance regressions caused by auto-roller commits
|
||||
|
||||
Although reverting a normal commit that caused performance regressions is the default
|
||||
behavior, reverting an [auto-roller](https://github.com/flutter/flutter/wiki/Autorollers)
|
||||
(e.g., an engine-roller commit like https://github.com/flutter/flutter/commit/fdcb57b69eff2162e9aead6dec0f8058788e7608)
|
||||
commit could cause some complications:
|
||||
|
||||
1. The auto-roller commit usually include multiple commits of the source repo (e.g., engine-roller
|
||||
commit includes multiple commits of https://github.com/flutter/engine). This can be applied
|
||||
recursively as the engine-roller commit includes a dart-roller commit, or a skia-roller commit.
|
||||
Therefore, a roller commit could actually include a ton of leaf-level commits, which makes it
|
||||
really hard to triage which leaf commit actually caused the regression.
|
||||
|
||||
2. The auto-roller will try to roll again as soon as possible that will reland any changes reverted
|
||||
by a Flutter commit revert. So in order to keep the revert effective, one has to either
|
||||
(1) pause the auto-roller, or (2) revert the leaf commit in the source repo.
|
||||
|
||||
3. If the auto-roller is paused for a long time (say 1 day), the source repo will accumulate many
|
||||
commits. That makes the next roll very hard to manage: it's difficult to triage a build failure
|
||||
or a new performance regression caused by the next roll, since that roll will include all the commits
|
||||
in the paused period.
|
||||
|
||||
Therefore, reverting a roller commit or pausing the auto-roller is
|
||||
*NOT* the default action if it causes a performance regression.
|
||||
The default action should be to file an issue with labels "performance", "regression", and `P0`
|
||||
immediately, and start investigating which leaf-commit caused the regression. Once the leaf-commit
|
||||
is identified, check if it's an expected trade-off. If so, remove the `P0` label and try to see
|
||||
if there's any way to mitigate the regression. If not, revert the leaf commit in the source repo
|
||||
and let the auto-roller apply that revert. Once the revert is rolled into Flutter, close the issue.
|
||||
|
||||
## Handling interdependent/multi-repo changes
|
||||
|
||||
If you're working on a feature that requires changes in both the [framework](https://github.com/flutter/flutter) and [engine](https://github.com/flutter/flutter) repos, you'll need to make 2 separate PRs. In such situation CI on the framework PR may fail because it depends on engine code that is not (yet) on engine repo's main branch. In such situation, you need to land the changes in the engine first, wait for them to roll into the framework's main branch, and then rebase your framework PR.
|
||||
|
||||
## Handling breaking changes
|
||||
|
||||
In general, we want to avoid making changes to Flutter, our plugins, or our packages, that force developers using Flutter to change their code in order to upgrade to new versions of Flutter. See [our compatibility policy](https://flutter.dev/docs/resources/compatibility).
|
||||
|
||||
Sometimes, however, doing this is necessary for the greater good. We want our APIs to be
|
||||
intuitive; if being backwards-compatible requires making an API into something that we would
|
||||
never have designed that way unless forced to by circumstances, then we should instead break
|
||||
the API and make it good.
|
||||
|
||||
The process for making breaking changes is as follows:
|
||||
|
||||
### 1. Determine if your change is a breaking change
|
||||
|
||||
Implement the change you wish to see and run the existing tests against your new code (without having changed the tests first). Changes that break (i.e. require changes to) one or more of the contributed tests are considered "breaking changes".
|
||||
|
||||
The "contributed tests" are:
|
||||
|
||||
* Those in the [`customer_testing`](https://github.com/flutter/tests) shard on `flutter/flutter` PRs.
|
||||
* Additional test suites that we have been allowed to run but that are not public. (Notably, Google allows us to run several tens of thousands of [proprietary tests](https://github.com/flutter/flutter/wiki/Understanding-Google-Testing) on each commit.)
|
||||
|
||||
There are no exemptions to this policy, because these tests run in our CI and breaking them will close the tree.
|
||||
|
||||
In cases where these tests pass but we can nonetheless imagine reasonable scenarios where developers would be affected negatively, by courtesy, once the change has landed, engineers are encouraged to announce the changes by sending an e-mail to [flutter-announce@](https://groups.google.com/g/flutter-announce), a message to the `#announcements` channel on our [Chat](Chat.md), and tagging the relevant issues with the [**c: API break** label](https://github.com/flutter/flutter/labels/c%3A%20API%20break) (such that they will be included in our release notes). However, we do not consider these breaking changes. (One reason to do this would be if we see our own tests being significantly affected, even if no contributed test actually fails.)
|
||||
|
||||
### 2. Evaluate the breaking change
|
||||
|
||||
If your change counts as a breaking change, seriously consider whether it is truly necessary and beneficial. Consider writing a [design document](Design-Documents.md). Discuss it with your code reviewer. Raise it in [Chat](Chat.md).
|
||||
|
||||
### 3. Prepare your change.
|
||||
|
||||
If you decide your change is valuable enough to deploy, adjust your PR so that it introduces the new functionality, API, behavior change, etc, in an opt-in fashion, thus avoiding the immediate breakage.
|
||||
|
||||
_For example, rather than replacing a widget with another, introduce the new widget and discourage use of the old one. Rather than changing the order in which a certain argument is processed, provide a flag that selects which order the arguments will be processed in._
|
||||
|
||||
When changing the semantics of an API with a temporary opt-in, a three-phase change is needed (adding the new API and opt-in, then removing the old API, then removing the opt-in.)
|
||||
|
||||
If possible, avoid four-phase deprecations (adding a new API with a temporary name and deprecating an old API, removing the old API, changing the new API to the old name and deprecating the temporary name, and finally removing the temporary name), because they involve a lot of churn and will irritate our developers.
|
||||
|
||||
Stage your change and the documentation for your change. Typically this will be two or more PRs, plus PRs to fix the tests that were broken (see step 1), as well as writing a migration guide as a PR to the Website repository.
|
||||
|
||||
If possible, include flutter fixes to aid users in migration. Whether or not the change is supported by flutter fix should be included in the migration guide. To learn about authoring fixes, see [Data driven Fixes](Data-driven-Fixes.md).
|
||||
|
||||
**Use our [breaking change migration guide template](https://github.com/flutter/website/blob/main/src/release/breaking-changes/template.md)** (follow all the instructions in the comments) to create the migration guide that describes the change. Do not land the migration guide at this time. You will need to update it before you land it in the last step.
|
||||
|
||||
### 4. Land your change.
|
||||
|
||||
Once you are ready, have received feedback, iterated on your design and your migration guide, land your initial change and start migrating clients. *Do not yet land the migration guide.* Once all the clients are migrated, land your final change. (You may have several iterations here if you have a multiphase roll-out.)
|
||||
|
||||
During this process, each individual PR does not break any tests, so it should not block any autorollers.
|
||||
|
||||
|
||||
### 5. Document the change, including clear documentation for migrating code, with samples, and clear rationales for each change
|
||||
|
||||
Once everything has landed:
|
||||
|
||||
* update your migration guide based on your experience migrating everyone,
|
||||
* update the timeline on the guide, and push it to [the flutter.dev Web site](https://flutter.dev/docs/release/breaking-changes) (don't forget to update the [index](https://github.com/flutter/website/blob/main/src/release/breaking-changes/index.md) of that directory as well),
|
||||
* e-mail a copy to [flutter-announce@](https://groups.google.com/g/flutter-announce),
|
||||
* notify the `#announcements` channel on our [Chat](Chat.md), and
|
||||
* add the [**c: API break** label](https://github.com/flutter/flutter/labels/c%3A%20API%20break) to the relevant issues, so they get listed in the upcoming Release notes.
|
||||
|
||||
### Deprecations
|
||||
|
||||
Old APIs can be marked as deprecated as part of this process. Deprecation is not a way to avoid making a breaking change; you should consider deprecating an API to be equivalent to removing it, as some of our customers (and we ourselves) consider using a deprecated API to be anathema (triggering a build failure).
|
||||
|
||||
The syntax for deprecations must match the following pattern:
|
||||
|
||||
```dart
|
||||
@Deprecated(
|
||||
'Call prepareFrame followed by owner.requestVisualUpdate() instead. '
|
||||
'This will enable an improvement to performance in a future version of Flutter. '
|
||||
'This feature was deprecated after v2.9.0-0.1.pre.'
|
||||
)
|
||||
```
|
||||
|
||||
In other words:
|
||||
|
||||
```dart
|
||||
@Deprecated(
|
||||
'[description of how to migrate] '
|
||||
'[brief motivation for why we are breaking the API] '
|
||||
'This feature was deprecated after [beta version at time of deprecation].'
|
||||
)
|
||||
```
|
||||
|
||||
Using this standard form ensures that we can write a script to detect all deprecated APIs and remove them. We have a test that verifies that this syntax is followed.
|
||||
|
||||
To determine the latest beta version, see <https://flutter.dev/docs/development/tools/sdk/releases>.
|
||||
|
||||
When adding a deprecation notice to the framework, a flutter fix should be included with your change. This helps users migrate to the new API as easily as possible. To learn more about authoring fixes, see [Data driven Fixes](Data-driven-Fixes.md). If a fix cannot be written for the new API, please file an issue in https://github.com/dart-lang/sdk and link to it in your change.
|
||||
|
||||
When deprecating features, be aware that *you* will not by default be informed when the Flutter code itself uses the deprecated feature (there is a `deprecated_member_use_from_same_package: ignore` line in the root `analysis_options.yaml` file). To find places where the old feature is used, rename its declaration and see where the compiler complains. (You can't just comment out the "ignore" in the `analysis_options.yaml` file because it's hiding hundreds of other warnings...)
|
||||
|
||||
Deprecations are removed in a consistent "first-in-first-out" fashion. The lifetime for a Flutter deprecation is 1 year after reaching the stable channel, or after 4 stable releases, whichever is longer. Deprecations are still subject to the policy described on the [breaking changes page](https://flutter.dev/docs/release/breaking-changes) of the website. Where possible, prepare the dart fix tools and write appropriate migrations guides.
|
||||
|
||||
This policy is currently enforced for `package:flutter`, `package:flutter_driver`, and `package:flutter_test`.
|
||||
|
||||
## Skipped Tests
|
||||
|
||||
Tests can be skipped using the `skip` parameter of `test()`, `group()` and `testWidgets()`. However, they should be kept to a minimum and only done for the following two reasons.
|
||||
|
||||
The first is if there is a test that is flaky, we can mark is as *temporarily* skipped to keep the tree green while a fix for it is developed. For these types of skips you need to file a tracking issue so we can ensure there is follow up to remove the skip. This tracking issue should be tagged with the `skip-test` label. Then in a comment on the same line as the parameter, include a link to this issue:
|
||||
|
||||
```dart
|
||||
skip: true, // https://github.com/flutter/flutter/issues/XXXXX
|
||||
```
|
||||
|
||||
The other reason to use the skip parameter is to mark a test that by design doesn't make sense to test under a specific condition. An example would be a test that only tests a feature available on a specific platform or environment. For these cases, include a comment on the same line as the skip parameter with the text `[intended]` and a short description of why the skip is needed:
|
||||
|
||||
```dart
|
||||
skip: isBrowser, // [intended] There are no default transitions to test on the web.
|
||||
```
|
||||
|
||||
If the analyzer script sees a skip without a comment containing either an issue link or an `[intended]` tag, it will report and error and fail the check.
|
||||
27
docs/contributing/What-should-I-work-on.md
Normal file
27
docs/contributing/What-should-I-work-on.md
Normal file
@@ -0,0 +1,27 @@
|
||||
This page attempts to be a one-stop shop for figuring out what the most important thing to work on is, so that team members (contributors) can determine the more effective way to improve Flutter.
|
||||
|
||||
1. Build breakage. Check the [dashboard](https://flutter-dashboard.appspot.com/build.html).
|
||||
1. [P0 issues](https://github.com/flutter/flutter/labels/P0) (e.g. serious regressions).
|
||||
1. Mentoring promising new contributors.
|
||||
1. [Code review of open PRs](https://github.com/pulls?utf8=%E2%9C%93&q=is%3Aopen+is%3Apr+archived%3Afalse+user%3Aflutter+).
|
||||
1. [P1 issues](https://github.com/flutter/flutter/labels/P1), including:
|
||||
1. [Flaky tests](https://github.com/flutter/flutter/issues?q=is%3Aopen+is%3Aissue+label%3A%22team%3A+flakes%22+sort%3Aupdated-asc).
|
||||
1. Performance regressions. Check the [dashboard](https://flutter-dashboard.appspot.com/benchmarks.html) for new unreported regressions and see GitHub for the list of [reported performance regressions](https://github.com/flutter/flutter/issues?utf8=%E2%9C%93&q=is%3Aopen+label%3A%22c%3A+performance%22+label%3A%22c%3A+regression%22+).
|
||||
1. [Other regressions](https://github.com/flutter/flutter/issues?q=is%3Aopen+is%3Aissue+label%3A%22c%3A+regression%22).
|
||||
1. Reducing technical debt. (For example, increasing [our test coverage](./testing/Test-coverage-for-package-flutter.md) by [writing new tests](./testing/Running-and-writing-tests.md), or fixing TODOs.)
|
||||
1. [P2 issues](https://github.com/flutter/flutter/labels/P1), which correspond to the remaining areas of our [roadmap](https://github.com/flutter/flutter/wiki/Roadmap), such as:
|
||||
* Bugs marked as [annoyances](https://github.com/flutter/flutter/issues?q=is%3Aopen+is%3Aissue+label%3A%22a%3A+annoyance%22+sort%3Areactions-%2B1-desc).
|
||||
* Bugs labeled as issues of [quality](https://github.com/flutter/flutter/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue+label%3A%22a%3A+quality%22+sort%3Areactions-%2B1-desc+).
|
||||
* Bugs with the [crash](https://github.com/flutter/flutter/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue+label%3A%22c%3A+crash%22+sort%3Areactions-%2B1-desc+) label.
|
||||
1. [Issues sorted by thumbs-up](https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc). Focus on bugs in existing code and avoid adding new code.
|
||||
1. Everything else. Consider [this advice](https://ln.hixie.ch/?start=1674863881&count=1) when prioritizing bugs.
|
||||
|
||||
Bugs in other bug systems should be tracked with bugs in GitHub. OKRs should be reflected in the items listed above. For example, OKRs should reflect what the roadmap covers, expected customer blockers, and so forth. Work that is unique to a particular quarter would be represented by a filed bug with a milestone and assignee.
|
||||
|
||||
During [triage](https://github.com/flutter/flutter/wiki/Triage), bugs should be prioritized according to the [P0-P3 labels](./issue_hygiene/README.md#priorities) so as to fit the order described above.
|
||||
|
||||
Sometimes, items in the list above escalate. For example, a bug might get filed as a P2 issue, then be recognized as a critical regression and upgraded to P0.
|
||||
|
||||
See also:
|
||||
|
||||
* [Issue Hygiene](./issue_hygiene/README.md), in particular the section on prioritization.
|
||||
@@ -0,0 +1,37 @@
|
||||
Animated GIFs are useful for documentating animations. Here’s how to make them:
|
||||
|
||||
## Capturing video of an Android device
|
||||
|
||||
* This feature is available on KitKat and later.
|
||||
* Make sure to launch the app using `flutter run --release` for faster performance.
|
||||
* Start recording video by running this command on the command line:
|
||||
|
||||
```adb shell screenrecord /sdcard/recording.mp4```
|
||||
|
||||
* Interact with the app. When you are done you can terminate the recording with CTRL+c.
|
||||
|
||||
* Pull the recording off the device and onto your local hard drive:
|
||||
|
||||
```adb pull /sdcard/recording.mp4 ~/Downloads/```
|
||||
|
||||
## Capturing video of the iOS simulator
|
||||
|
||||
* Move the iOS simulator above your other applications
|
||||
* Launch the "QuickTime Player" app
|
||||
* Select “New Screen Recording” from the File menu
|
||||
* Draw a box around the Simulator and click "Start Recording"
|
||||
* When you're finished, click on Stop Recording on the Menu Bar.
|
||||
* If needed, trim your recording using "Trim" from the "Edit" menu.
|
||||
* Save the recording
|
||||
|
||||
## Converting the recording to GIF
|
||||
|
||||
* Go to http://ezgif.com/video-to-gif and upload the video.
|
||||
* If further trimming is needed, select the start and end times for your GIF.
|
||||
* Select an appropriately small size for the GIF (recommended: 320xAUTO).
|
||||
* Select a high framerate for the GIF (the max is 20) for smoothest animation
|
||||
* Convert to GIF and download the GIF file
|
||||
|
||||
## Example
|
||||
|
||||

|
||||
82
docs/contributing/issue_hygiene/Popular-issues.md
Normal file
82
docs/contributing/issue_hygiene/Popular-issues.md
Normal file
@@ -0,0 +1,82 @@
|
||||
When deciding what to work on, we usually focus on issues that have a lot of thumbs-up reactions on the first comment, what we call the "popular issues".
|
||||
|
||||
Some popular issues are topics on which we cannot find a good way to make progress. Since those issues where we _do_ make progress get closed, the result is that [the list of most-popular issues](https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc) is now full of issues where we have conspicuously not made progress!
|
||||
|
||||
In the interests of transparency, this wiki page discusses the status of the ten most popular issues. It is only updated occasionally and so may not be entirely up to date; for the most up to date information, please see the latest comments on the relevant issue. (Please avoid asking for an update on issues, otherwise they become full of people asking for updates and nobody can find the actual updates.)
|
||||
|
||||
_See also: [the list of popular issues including closed issues](https://github.com/flutter/flutter/issues?q=is%3Aissue+sort%3Areactions-%2B1-desc), which shows that popular issues do get closed. 😅_
|
||||
|
||||
## [Code Push / Hot Update / out of band updates](https://github.com/flutter/flutter/issues/14330) (#14330)
|
||||
|
||||
<!-- https://github.com/flutter/flutter/issues/14330#issuecomment-442274897 (terminology) -->
|
||||
<!-- https://github.com/flutter/flutter/issues/14330#issuecomment-485565194 (2019 update) -->
|
||||
<!-- https://github.com/flutter/flutter/issues/14330#issuecomment-442274897 (rfw) -->
|
||||
|
||||
There are three main areas that people are referring to here:
|
||||
|
||||
* **Modular application delivery**: the ability to package a single app into multiple separate archives when compiling it, and download them independently as needed. This is supported on Android via [deferred components](https://docs.flutter.dev/perf/deferred-components). We suspect it is not possible to achieve this on iOS with Apple's current guidelines and tools. We have not yet attempted to provide this on desktop or web, primarily because we have more important issues to resolve on those platforms first.
|
||||
|
||||
* **Dynamic extension loading**: the ability to download some Dart code that wasn't written when the app was first published, which adds a new feature to the app. This could be done on the fly. It may require the core app to be larger since we can't know ahead of time what is needed by each future extension. There are various solutions in this space, such as combining [the rfw package](https://pub.dev/packages/rfw) and an FFI-based or Wasm-based solution (e.g. [package:wasm](https://pub.dev/packages/wasm)). There is [an example](https://github.com/flutter/packages/tree/main/packages/rfw/example/wasm) that provides a proof-of-concept for this combination of packages: a Flutter desktop application that knows nothing about being a calculator downloads an interface description specifying all the buttons and their layout to show on the screen, and downloads a C program compiled to Wasm to perform the calculations. The Flutter program is merely a bridge between these two downloaded files. We are looking for feedback from people using this feature; please add your experiences to [issue 90218](https://github.com/flutter/flutter/issues/90218).
|
||||
|
||||
* **Dynamic patching**: the ability to update the Dart code of an app in the field by downloading a patch (of sorts) and providing it to the Dart VM. This would require a reload of the app to take effect. Dynamic patching was previously on our roadmap for 2019. After investigating this in greater detail, we decided not to proceed with that work. There were several factors that led us to this decision:
|
||||
|
||||
* To comply with our understanding of store policies on Android and iOS, any solution would be limited to JIT code on Android and interpreted code on iOS. We are not confident that the performance characteristics of such a solution on iOS would reach the quality that we demand of our product. (In other words, "it would be too slow".)
|
||||
|
||||
* There are some serious security concerns. Since these patches would essentially allow arbitrary code execution, they would be extremely attractive malware vectors. We could mitigate this by requiring that patches be signed using the same key as the original package, but this is error prone and any mistake would have serious consequences. This is, fundamentally, the same problem that has plagued platforms that allow execution of code from third-party sources. This problem could be mitigated by integrating with a platform update mechanism, but this defeats the purpose of an out-of-band patching mechanism.
|
||||
|
||||
* There is currently no out-of-the-box open source hosting solution for patching applications, so we would either have to rely on people configuring their Web servers accordingly, or we would have to create integrations for proprietary third-party services, or we would have to create our own bespoke solution. Hosting patches is a space we are not eager to enter. Having people configure their own server leaves them open to making mistakes with potentially serious implications as explained in the previous point about security. Depending on third-party services puts Flutter in an awkward position of having to pick winners and exposes us to the risk of those projects themselves making policy changes that would affect this feature.
|
||||
|
||||
## [Let flutter be installable via homebrew](https://github.com/flutter/flutter/issues/14050) (#14050)
|
||||
|
||||
<!-- https://github.com/flutter/flutter/issues/14050#issuecomment-1012647917 -->
|
||||
|
||||
Currently, we see this as a lower priority than our other release-related work (such as working towards [SLSA compliance](https://slsa.dev)). There are a number of other mechanisms for obtaining Flutter today, so this does not immediately unblock anyone, it is "merely" a convenience. That said, we recognize that homebrew is a pretty idiomatic way of getting software for developers on macOS, and so the request is quite valid.
|
||||
|
||||
If anyone would be interested in implementing an official homebrew installation path, the best thing to do would be to reach out on the #hackers-releases channel of our Discord (see [Chat](../Chat.md)). Implementing it would require integrating into our release pipeline, so familiarity with that would be extremely helpful. It would also require carefully negotiating how Flutter's primary distribution mechanism (shipping the `git` repo directly) should interact with Homebrew's mechanisms, so familiarity with both of those would also be needed.
|
||||
|
||||
## [Design a new vector file format](https://github.com/flutter/flutter/issues/1831) (#1831)
|
||||
|
||||
A [design document](https://flutter.dev/go/vector-graphics) containing both a detailed study of the problem and a strawman proposal have been published (comments welcome). The primary goal of the strawman proposal is to see if it is possible to create a format that is implemented entirely on the GPU (the thought being that creating yet another CPU-bound format doesn't really bring the industry forward). The next step is to experiment with implementing the proposal. Unfortunately all our shader experts are currently busy on higher-priority problems (like improving rendering performance and reducing jank), so this work has stalled.
|
||||
|
||||
As usual, contributions are welcome. Reach out to Hixie directly (either by e-mail, ian@hixie.ch, or on our [Chat](../Chat.md) channels) if you are interested in helping out.
|
||||
|
||||
## [Enable "hot reload" (not just "hot restart") for Flutter Web](https://github.com/flutter/flutter/issues/53041) (#53041)
|
||||
|
||||
Nobody has currently volunteered to work on this. Long term web efforts are primarily focused on Wasm, so we don't expect
|
||||
to see progress soon. It is a technically extremely difficult and subtle problem.
|
||||
|
||||
## [Improve the indexability (SEO) of Flutter apps on the web](https://github.com/flutter/flutter/issues/46789) (#46789)
|
||||
|
||||
This feature is one that is recognized as important. There are some prerequisites, like improving Flutter's deep linking and accessibility features, which we have to deal with first. There are also other issues, like those around performance, plugins, and embedding, that are currently higher on the list for people who are currently contributing to Flutter's web support.
|
||||
|
||||
Fixing this issue is non-trivial, as Flutter's architecture is one that is fundamentally different than what the web usually expects. If you are interested in contributing, the best place to begin would be to discuss potential approaches on our #hackers-web [Chat](../Chat.md) channel, followed by writing up a design doc (the process for which is also on the [Chat](../Chat.md) page).
|
||||
|
||||
## [Bring Material 3 to Flutter](https://github.com/flutter/flutter/issues/91605) (#91605)
|
||||
|
||||
Work continues to implement the latest version of Material Design in Flutter's framework, we are making great progress here!
|
||||
|
||||
## [Apple CarPlay / Android Auto support?](https://github.com/flutter/flutter/issues/26801) (#26801)
|
||||
|
||||
For Apple CarPlay, there is a package called [flutter_carplay](https://pub.dev/packages/flutter_carplay) by Oğuzhan Atalay that gives a Flutter API for controlling the CarPlay API. It's not clear that anything beyond that really makes sense for CarPlay, because Apple's API is template based and so Flutter (with its rendering engine, widgets framework, platform neutrality, etc) doesn't really provide any direct value.
|
||||
|
||||
For Android Auto, our understanding is that the situation is similar: there are some templates that you can fill in using Android APIs. To our knowledge nobody has yet created a plugin to expose those APIs to Dart code, but we are not aware of any reason why that would not be possible. (For extra bonus points, one could imagine a package that tries to intelligently fill both CarPlay and Android Auto templates from the same source data, but that may be hard if the templates are too different from each other.)
|
||||
|
||||
Currently, it does not seem that we can provide substantially more value here than anyone else could in writing plugins like Oğuzhan's, so we do not intend to work on this. We would encourage people to work with Oğuzhan and/or create new plugins for these features. Should the situation change (e.g. if CarPlay or Android Auto supported a way for us to directly send pixels to the car dashboard, allowing the power of Flutter's widgets to be useful here), we would reconsider this feature.
|
||||
|
||||
## [Server-side rendering for Flutter web](https://github.com/flutter/flutter/issues/47600) (#47600)
|
||||
|
||||
Fundamentally, rendering Flutter web apps to HTML is incompatible with Flutter's current architecture, and therefore this is not something we are likely to ever attempt. It's also not something we think is particularly useful. We see Flutter as the first of a new breed of frameworks that target WebGL and Wasm and leave HTML behind. For more detailed thoughts, see [the status update on the issue](https://github.com/flutter/flutter/issues/47600#issuecomment-1016920547).
|
||||
|
||||
We believe indexability (SEO) can be addressed without server-side rendering; see the issue above for a discussion of that topic.
|
||||
|
||||
## [Automatic/scalable shader warm-up](https://github.com/flutter/flutter/issues/32170) (#32170)
|
||||
|
||||
Shader warm-up is available on every platform. To automate the generation of shader warm-up files currently requires first manually writing automated tests, and then running these in CI.
|
||||
|
||||
Our medium-term efforts are around removing the need for shader warm-up entirely, and therefore we are not currently planning on working on further automating the creation of shader warm-up files (no point working on something that we want to make irrelevant).
|
||||
|
||||
If we manage to remove the need for shader warm-up entirely, we will close issue #32170. If not, we will reconsider whether additional efforts to automate shader warm-up file generation are warranted.
|
||||
|
||||
## [OpenContainer jank due to multiple frames of shader compilation](https://github.com/flutter/flutter/issues/76180) (#76180)
|
||||
|
||||
A huge area of focus for us right now is our new graphics backend, [Impeller](https://docs.flutter.dev/perf/impeller), which should remove all runtime shader compilation, thus entirely removing this source of jank.
|
||||
387
docs/contributing/issue_hygiene/README.md
Normal file
387
docs/contributing/issue_hygiene/README.md
Normal file
@@ -0,0 +1,387 @@
|
||||
## tl;dr
|
||||
|
||||
- Avoid asking about the status of an issue; if we have an update, we'll post it.
|
||||
- If you have permission, assign bugs to yourself if you're working on them.
|
||||
- Unassign bugs that you are not working on soon.
|
||||
- If an issue is not assigned, assume it is available to be worked on.
|
||||
|
||||
## Overview
|
||||
|
||||
We use three issue trackers: the [main one on flutter/flutter](https://github.com/flutter/flutter/issues), one for [the flutter.dev Website, on flutter/website](https://github.com/flutter/website/issues), and one for [the IntelliJ and Android Studio plugins, on flutter/flutter-intellij](https://github.com/flutter/flutter-intellij/issues).
|
||||
|
||||
This page mostly talks about how we handle things for the flutter/flutter issue tracker.
|
||||
|
||||
### Issue philosophy
|
||||
|
||||
We assume that Flutter, like all non-trivial software, has an infinite number of bugs. The issue tracker contains the list of bugs that we are very lucky to have had reported by our generous community. Bugs includes known defects, as well as feature requests, planned work, and proposals.
|
||||
|
||||
Within the bug database we try to make sure each issue is actionable and discoverable. We do this by carefully updating the issue subject line, making sure every issue has steps to reproduce, and using labels to categorize the issue in ways that can be found by GitHub search.
|
||||
|
||||
|
||||
## Comments
|
||||
|
||||
### Do not add "me too" or "same" or "is there an update" comments to bugs
|
||||
|
||||
The Flutter team prioritizes issues in part based on the number of +1 (thumbs
|
||||
up) reactions on the top level comment of the bug. Adding comments like "me
|
||||
too" or "same here" is generally distracting and makes it harder to find
|
||||
other more meaningful content in the bug. If you have no new details to add,
|
||||
consider just thumbs up-ing the issue. If you wish to subscribe to the issue,
|
||||
click the "subscribe" button in the right hand column of the GitHub UI.
|
||||
|
||||
Adding comments explaining how a bug is dire and how you will stop using Flutter
|
||||
if it is not fixed is upsetting for the engineers working on Flutter (many of
|
||||
whom are volunteers, not that being paid to work on Flutter makes such comments
|
||||
any less upsetting). Out of a respect for the team, and as required by our [code
|
||||
of conduct](https://github.com/flutter/flutter/blob/main/CODE_OF_CONDUCT.md), we
|
||||
ask that you avoid adding comments that are not actively helpful. There are other
|
||||
venues if you want to complain without being constructive.
|
||||
|
||||
Asking for updates is also not generally helpful, because it just leads to issues
|
||||
being full of comments asking for updates and that makes finding useful information
|
||||
in a bug harder (an exception might be if you are participating in the triage process,
|
||||
but even then consider reaching out to people directly if possible). If you believe
|
||||
there could be information that has not been posted, ask on our Discord server instead
|
||||
(see [Chat](../Chat.md)).
|
||||
|
||||
### Issues are not always the best venue for discussions
|
||||
|
||||
Discussions within an issue should remain focused on the topic, specifically about what the filed issue is and how to solve it. Broader discussions are best suited to happen on Discord (see [Chat](../Chat.md)) or in design docs using Google Docs (see [Design Documents](../Design-Documents.md)). This is because GitHub hides comments, doesn't have threading, notifications get lost in the swamp of other GitHub e-mails, etc.
|
||||
|
||||
If you move to another tool for part of the discussion, remember to add a summary of the discussion and document any decisions that took place. This allows people following the issue to keep updated and continue to participate.
|
||||
|
||||
Issues are never an appropriate venue for asking for help with your code. Issues are also not a good venue for discussing project direction.
|
||||
|
||||
### Comments providing workarounds
|
||||
|
||||
Providing workarounds for issues can be helpful for developers using Flutter and finding a bug,
|
||||
but please keep such comments to a minimum so as to avoid disrupting the engineers trying to
|
||||
fix the issue. Rather than discussing workarounds, provide a pointer to another forum
|
||||
(e.g. Stack Overflow) where workarounds and temporary solutions are more appropriate. Thanks.
|
||||
However, when a workaround has been identified, consider applying the `workaround available` label to make that info readily available.
|
||||
|
||||
### Avoid posting screenshots of text
|
||||
|
||||
If you want to show code, quote someone, or show a string of text that does
|
||||
not render properly with Flutter, please avoid sharing it via an image or
|
||||
screenshot. Text in images cannot be copied, and cannot be automatically
|
||||
translated via services like Google Translate. This makes it harder for team
|
||||
members who do not speak that language to participate in the issue.
|
||||
|
||||
It is perfectly fine to share a screenshot of text rendering invalidly, but
|
||||
also include the actual string or character(s) that lead to it so that they
|
||||
can be copied and pasted into a test case.
|
||||
|
||||
### Provide reduced test cases
|
||||
|
||||
To debug a problem, we will need to be able to reproduce it. The best way
|
||||
to help us do that is to provide code, licensed according to [the BSD license
|
||||
used by Flutter](https://github.com/flutter/flutter/blob/main/LICENSE), that
|
||||
has been reduced as far as possible (such that removing anything further stops
|
||||
showing the bug). Attach such a file or files to the issue itself.
|
||||
|
||||
For legal reasons, we cannot debug problems that require looking at proprietary
|
||||
code or, generally, code that is not publicly available.
|
||||
|
||||
### Consider posting issues in English
|
||||
|
||||
If you are able to read and write English clearly, consider posting your issue
|
||||
in English, even if it is about a language specific issue (like the way text
|
||||
renders in some non-English language).
|
||||
|
||||
It is fine to post issues in languages other than English, but consider that
|
||||
many readers will rely on automatic translation services to read your issue.
|
||||
Please avoid using screenshots in languages other than English, as services like
|
||||
Google Translate will not translate the text in images, and the pool of people
|
||||
able to assist you will be reduced.
|
||||
|
||||
|
||||
## Locking an issue
|
||||
|
||||
**Closed** issues that haven't received any activity in a [few weeks](https://github.com/flutter/flutter/blob/main/.github/lock.yml#L4)
|
||||
are automatically locked by a [bot](https://github.com/apps/lock). This is
|
||||
done to encourage developers to file new bugs, instead of piling comments
|
||||
on old ones.
|
||||
|
||||
Under normal circumstances, open issues should not regularly be locked. The most
|
||||
common reason for manually locking an open issue is that issue is well
|
||||
understood by the engineers working on it,
|
||||
is believed to be appropriately prioritized, has a clear
|
||||
path to being fixed, and is otherwise attracting
|
||||
a lot of off-topic or distracting comments like "me too" or
|
||||
"when will this be fixed" or "I have a similar issue that might
|
||||
or might not be the same as this one".
|
||||
|
||||
If you are concerned that such an issue is not receiving its due
|
||||
attention, see Escalating an Issue, described above. If you are
|
||||
not already a contributor but would like to work on that issue,
|
||||
consider reaching out on an appropriate [chat](../Chat.md).
|
||||
|
||||
If you have a similar issue and are not sure if it is the same,
|
||||
it is fine to file a new issue and linking it to the other issue.
|
||||
Please avoid intentionally filing duplicates.
|
||||
|
||||
Very rarely, an issue gets locked because discussion has become
|
||||
unproductive and has repeatedly violated the [Code of Conduct](https://github.com/flutter/flutter/blob/main/CODE_OF_CONDUCT.md).
|
||||
|
||||
|
||||
## Priorities
|
||||
|
||||
**The [`P0`](https://github.com/flutter/flutter/labels/P0) label** indicates that the issue is one of the following:
|
||||
* a build break, regression, or failure in an existing feature that prevents us from shipping the current build.
|
||||
* an important item of technical debt that we want to fix promptly because it is impacting team velocity.
|
||||
* an issue blocking, or about to block, a top-tier customer. (See [below](#customers) under "customers" for a definition of "top-tier customer".)
|
||||
|
||||
There are generally less than twenty-five P0 bugs (one GitHub search results page). If you find yourself assigning a P0 label to an issue, please be sure that there's a positive handoff between filing and a prospective owner for the issue.
|
||||
|
||||
Issues at this level should be resolved in a matter of weeks and should have weekly updates on GitHub.
|
||||
|
||||
During normal work weeks (e.g. not around the new year), issues marked P0 get audited weekly during the "critical triage" meeting to ensure we do not forget about them. Issues marked P0 should get updates at least once a week, to keep the rest of the team (and anyone affected by the issues) apprised of progress.
|
||||
|
||||
**The [`P1`](https://github.com/flutter/flutter/labels/P1) label** indicates high-priority issues that are at the top of the work list. This is the highest priority level a bug can have if it isn't affecting a top-tier customer or breaking the build. Bugs marked P1 are generally actively being worked on unless the assignee is dealing with a P0 bug (or another P1 bug).
|
||||
|
||||
Issues at this level should be resolved in a matter of months and should have monthly updates on GitHub.
|
||||
|
||||
**The [`P2`](https://github.com/flutter/flutter/labels/P2) label** indicates issues that we agree are important to work on, but are not at the top of the work list. This is the default level for new issues. A bug at this priority level may not be fixed for a long time. Sometimes an issue at this level will first migrate to P1 before we work on them, but that is not required.
|
||||
|
||||
**The [`P3`](https://github.com/flutter/flutter/labels/P3) label** indicates issues that we currently consider less important to the Flutter project. We use "thumbs-up" on these issues as a signal when discussing whether to promote them to P2 or higher based on demand. (Of course, this does not mean the issues are not important to _you_, just that we don't view them as the especially important for Flutter itself.)
|
||||
|
||||
Typically we would accept PRs for `P3` issues (assuming they follow our [style guide](../Style-guide-for-Flutter-repo.md) and follow our [other rules](../Tree-hygiene.md)). Issues marked with the [`would require significant investment`](https://github.com/flutter/flutter/issues?q=is%3Aopen+is%3Aissue+label%3A%22would+require+significant+investment%22) label may require more than just a PR, for example, adding support for a whole new platform will require a commitment to provide CI resources for build and test, and someone to own the maintenance of those systems.
|
||||
|
||||
### When will my bug be fixed?
|
||||
|
||||
Flutter is an open source project and many people contribute their time (or their employees' time) to fix code and implement features. Typically, people fix bugs that are relevant to their customers. For example, Google engineers who contribute to Flutter are going to prioritize issues that affect Flutter apps written by Google teams. Many of us, however, also volunteer time to fix more general issues.
|
||||
|
||||
To determine when a bug will be fixed, look at the issue.
|
||||
|
||||
If there's a recent status update on the issue, that is the best information we have about the bug. If there's a lot of comments on the issue, we try to link to the latest status from the top comment, so look there. (Please [don't _ask_](#do-not-add-me-too-or-same-or-is-there-an-update-comments-to-bugs) for updates, though.)
|
||||
|
||||
If the issue is labeled with priorities `P0` or `P1`, or if the issue is assigned, we are likely to address it in the near term; we just need to find time.
|
||||
|
||||
Otherwise, we don't know when we're going to fix it. We may never get to it. In general, `P2` bugs are seen as more important than `P3` bugs. See the more detailed definitions of _priorities_ above.
|
||||
|
||||
_See also [Popular issues](../issue_hygiene/Popular-issues.md)._
|
||||
|
||||
### Escalating an issue that has the wrong priority
|
||||
|
||||
If you have a relationship with the Flutter team, raise the issue with
|
||||
your contact if you think the priority should be changed.
|
||||
|
||||
If you don't, consider finding like-minded developers to either implement
|
||||
the feature as a team, or to fund hiring someone to work on the feature,
|
||||
or to [mark the issue with a thumbs-up reaction](#thumbs-up-reactions).
|
||||
|
||||
Please don't comment on an issue to indicate your interest. Comments should
|
||||
be reserved for making progress on the issue.
|
||||
|
||||
|
||||
### Thumbs-up reactions
|
||||
|
||||
To vote on an issue, use the "Thumbs-up" emoji to react to the issue.
|
||||
|
||||
When examining issues, we use the number of thumbs-up reactions to an issue to determine an issue's relative popularity.
|
||||
This is, of course, but one input.
|
||||
At the end of the day, Flutter is an open source project and everyone (or every company) who contributes does so to further their own needs.
|
||||
To the extent that those needs are aligned with making Flutter popular,
|
||||
they tend to let their priorities be influenced by the "thumbs-up" reactions,
|
||||
but if you have something on which your business depends,
|
||||
the best solution is to pay someone to work on it.
|
||||
|
||||
See also:
|
||||
|
||||
* [All open issues sorted by thumbs-up](https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc)
|
||||
|
||||
* [Feature requests by thumbs-up](https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc+label%3A%22c%3A+new+feature%22)
|
||||
|
||||
* [Bugs by thumbs-up](https://github.com/flutter/flutter/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc+-label%3A%22c%3A+new+feature%22+)
|
||||
|
||||
We ignore other emoji reactions.
|
||||
|
||||
|
||||
|
||||
## Labels
|
||||
|
||||
We use [many labels](https://github.com/flutter/flutter/labels).
|
||||
|
||||
### Naming conventions
|
||||
|
||||
Common naming conventions for labels include:
|
||||
- **`a: *`** - The `a` ("area") prefix is used for labels that are about a specific topic that could span different layers of Flutter's implementation (for example "accessibility" or "text input").
|
||||
- **`browser: *`** - Indicates the browser for browser-specific issues for the web port of Flutter.
|
||||
- **`c: *`** - The `c` ("category") prefix says what kind of bug we're looking at (regression, crash, new feature request, etc).
|
||||
- **`d: *`** - The purple `d` ("devtools") labels are for organizing our developer tool issues.
|
||||
- **`d: *`** - The green `d` ("documentation") labels are for organizing our documentation-related issues.
|
||||
- **`dependency: *`** - Indicates the upstream team for issues that are blocked on some work from an upstream project (e.g. Skia, Dart).
|
||||
- **`e: *`** - The `e` ("engine") prefix is for subsets of the Flutter engine ([flutter/engine](https://github.com/flutter/engine)).
|
||||
- **`f: *`** - The `f` ("framework") prefix is for subsets of the Flutter framework ([flutter/flutter's packages/flutter/](https://github.com/flutter/flutter/tree/main/packages/flutter)).
|
||||
- **`found in release: x.yy`** - Used for a series of labels that indicate which versions of Flutter an issue was found in.
|
||||
- **`from: *`** - Labels that indicate where an issue originated (e.g. research, postmortems), if it wasn't filed organically.
|
||||
- **`t: *`** - The `t` ("tool") prefix is for subsets of the Flutter tool ([flutter/flutter's packages/flutter_tools/](https://github.com/flutter/flutter/tree/main/packages/flutter_tools)).
|
||||
- **`p: *`** - The `p` ("package") prefix is for specific packages ([flutter/packages](https://github.com/flutter/packages)). Light teal for packages and darker teal for plugins.
|
||||
- **`platform-*`** - The `platform` prefix is for bugs that are specific to one or more platforms.
|
||||
- **`r: *`** - The `r` ("resolution") prefix is used for labels that describe why an issue was closed.
|
||||
|
||||
### Adding labels
|
||||
|
||||
Labels are more or less free, so we can add them pretty easily. Please mention it to other team members first, so that they know what you are planning and can give feedback (please at a minimum mention it on `#hidden-chat` in our [Chat](../Chat.md)). Please make sure labels use a consistent color and naming scheme (e.g. all the framework-related labels are blue and start with `f:`).
|
||||
|
||||
Labels should be used for adding information to a bug. If you plan to use a label to find all instances of a particular topic (e.g. finding all PRs where someone wrote a design doc), be aware that there's no way to force people to label issues or PRs. You can, however, rely on automation to do it, for example we have a script that labels all PRs that affect the framework.
|
||||
|
||||
### Customers
|
||||
|
||||
The Flutter team is formed of engineers from many sources, including dedicated volunteers and employees of companies like Google. Each of these may have different ideas of who their customers are. For example, Google engineers consider some Google teams to be their customers, but someone who contributes on a code-for-hire basis may have their own customers.
|
||||
|
||||
Some teams using Flutter have a special relationship with some members of the Flutter team (e.g. they're collaborating with us on a new feature, or they're working with us on a product demo for an upcoming event). This is usually a fairly short-term arrangement for a specific business purpose. We provide such customers with a label (`customer: ...`) in our GitHub issue tracker. When these customers are working closely with members of the Flutter team, we may consider them "top-tier customers" for the purposes of prioritization.
|
||||
|
||||
Priority `P0` (see below) is sometimes used for bugs that affect these top-tier customers.
|
||||
|
||||
#### Coordinating between bug systems
|
||||
|
||||
Some customers have their own separate bug systems, in which they track Flutter
|
||||
issues. We consider our GitHub issue list to be canonical. However, if there
|
||||
is a link from the issue in our bug system to the customer's issue in their bug
|
||||
system, and we have been granted access, we will follow that link and may
|
||||
communicate in that separate bug system when attempting to track down the issue.
|
||||
|
||||
#### Special customer labels
|
||||
|
||||
The `customer: product` label is used to bring issues that product management
|
||||
and senior leads want resolved to the attention of the appropriate engineering
|
||||
team.
|
||||
|
||||
The `customer: crowd` label is used to represent bugs that are affecting large
|
||||
numbers of people; during initial [Triage](https://github.com/flutter/flutter/wiki/Triage), high-profile bugs get labeled in
|
||||
this way to bring them to the attention of the engineering team. "Large numbers"
|
||||
is a judgement call. If dozens of people independently run into the same issue
|
||||
and file a bug and we end up realizing that they're all duplicates of each other,
|
||||
then that would be a good candidate. On the other hand, if there is an active
|
||||
campaign to get people to comment on a bug, then it's probably not legitimately
|
||||
a `customer: crowd` bug, because people generally report bugs without having to
|
||||
be convinced to do so.
|
||||
|
||||
In general, a bug should only be marked `customer: crowd` `P0` if it
|
||||
is so bad that it is literally causing large numbers of people to consider changing
|
||||
careers.
|
||||
|
||||
|
||||
#### Other noteworthy labels
|
||||
|
||||
The `blocked` label can be used to indicate that a particular issue is unable to make progress until some other problem is resolved. This is particularly useful if you use your own list of assigned issues to drive your work.
|
||||
|
||||
The `good first issue` label should be used on issues that seem like friendly introductions to contributing to Flutter. They should be relatively well-understood issues that are not controversial, do not require a design doc, and do not require a deep understanding of our stack, but are sufficiently involved that they at least require a basic test to be added.
|
||||
|
||||
|
||||
## Milestones
|
||||
|
||||
We do not use GitHub milestones to track work.
|
||||
|
||||
|
||||
## Assigning Issues
|
||||
|
||||
Issues are typically self-assigned. Only assign a bug to someone else if
|
||||
they have explicitly volunteered to do the task. If you don't have permissions
|
||||
to assign yourself an issue you want to work on, don't worry about it, just
|
||||
submit the PR (see [Tree Hygiene](../Tree-hygiene.md)).
|
||||
|
||||
Only assign a bug to yourself when you are actively working on it
|
||||
or scheduled to work on it. If you don't know when you'll be working
|
||||
on it, leave it unassigned. Similarly, don't assign bugs to
|
||||
people unless you know they are going to work on it. If you find
|
||||
yourself with bugs assigned that you have not scheduled specific time
|
||||
to work on, unassign the bug so that other people feel
|
||||
empowered to work on them.
|
||||
|
||||
_Do_ assign a bug to yourself if you are working on it, or if you have
|
||||
scheduled time to work on it and are confident you will do so! This is how
|
||||
people can figure out what is happening. It also prevents duplicate
|
||||
work where two people try to fix the same issue at once.
|
||||
|
||||
You may hear team members refer to "licking the cookie". Assigning a
|
||||
bug to yourself, or otherwise indicating that you will work on it,
|
||||
tells others on the team to not fix it. If you then don't work on it,
|
||||
you are acting like someone who has taken a cookie,
|
||||
licked it to be unappetizing to other people, and then not eaten it.
|
||||
By extension, "unlicking the cookie" means indicating to the
|
||||
rest of the team that you are not actually going to work on the bug
|
||||
after all, e.g. by unassigning the bug from yourself.
|
||||
|
||||
## File bugs for everything
|
||||
|
||||
File bugs for anything that you come across that needs doing. When you
|
||||
implement something but know it's not complete, file bugs for what you
|
||||
haven't done. That way, we can keep track of what still needs doing.
|
||||
|
||||
### Exceptions
|
||||
|
||||
Do _not_ file bugs that meet the following criteria:
|
||||
|
||||
- Asking meta-questions like "why was bug #XYZ closed?" Instead, post
|
||||
on the original issue or raise the actual problem that is still not
|
||||
resolved.
|
||||
- Intentional duplicates like "This is the same as bug #ABC but that
|
||||
one is not getting enough attention." Instead, upvote the original
|
||||
issue or add a comment that provides new details that are not already
|
||||
captured or (best of all) assign it to yourself and start working on it!
|
||||
|
||||
### How to propose a specific change
|
||||
|
||||
If you have an idea that you would like to land, the recommended process is:
|
||||
|
||||
1. [File a bug](https://github.com/flutter/flutter/issues/new/choose) describing the problem.
|
||||
2. Write a [design doc](https://flutter.dev/go/template) that references this problem and describes your solution.
|
||||
3. Socialize your design on the bug you filed and on [Chat](../Chat.md). Collect feedback from various people.
|
||||
4. Once you have received feedback, if it is mostly positive, implement your idea and submit it. See the [Tree Hygiene](../Tree-hygiene.md) wiki page for details on submitting PRs.
|
||||
|
||||
### Every issue should be actionable
|
||||
|
||||
Avoid filing issues that are on vague topics without a clear problem description.
|
||||
|
||||
Please close issues that are not actionable. See [Triage](https://github.com/flutter/flutter/wiki/Triage) for more details.
|
||||
|
||||
#### Issues should have clear steps to reproduce
|
||||
|
||||
Every issue should have a clear description of the steps to reproduce the problem, the expected results, and the actual results.
|
||||
|
||||
If an issue is lacking this information, request it from the commenter and close the issue if information is not forthcoming.
|
||||
|
||||
## Closing issues
|
||||
|
||||
An issue should be closed if:
|
||||
|
||||
* it is fixed!
|
||||
* it is a [duplicate](https://github.com/flutter/flutter/wiki/Triage#duplicates).
|
||||
* it makes multiple requests which could be addressed independently. Encourage people to file separate bugs for each independent item.
|
||||
* it is describing a _solution_ rather than a _problem_. For example, it has no use cases, and the use cases are not obvious, or might have other solutions.
|
||||
* it is not [actionable](https://github.com/flutter/flutter/wiki/Triage#what-makes-an-issue-actionable) and does not [have unusual symptoms](https://github.com/flutter/flutter/wiki/Triage#unactionable-bugs-with-unusual-symptoms). This covers a wide variety of cases, such as invalid bugs, bugs without steps to reproduce, bugs that have become irrelevant, or bugs that are unclear and which the reporter has not offered more details for. It also includes non-catastrophic bugs that cannot be reproduced by anyone but the original reporter. For this latter case, encourage the reporter to attempt to debug the issue themselves, potentially giving suggestions for places where they could instrument the code to find the issue, and invite them to join the Discord for help; then add the `waiting for customer response` label. The issue will get automatically closed after a few weeks if they don't respond.
|
||||
* it is a feature request that we are unlikely to ever address, and if we did address it, it would not be part of the core SDK (e.g. it would be in a package). (For example, anything in the [`would be a good package` `P3`](https://github.com/flutter/flutter/issues?q=is%3Aopen+is%3Aissue+label%3A%22would+be+a+good+package%22+label%3AP3) list is a good candidate for closing without fixing.)
|
||||
* we would not accept a fix even if one were to be offered ([e.g. support for platforms at level of support 4](https://github.com/flutter/flutter/wiki/Values#levels-of-support)).
|
||||
* it is an issue regarding internal processes, tooling, or infrastructure (i.e. something that our users are not affected by), that we have no plans to get to (e.g. that would be marked P3). (For example, anything in the [`c: tech-debt` `P3`](https://github.com/flutter/flutter/issues?q=is%3Aopen+is%3Aissue+label%3A%22c%3A+tech-debt%22+label%3AP3) list is a good candidate for closing.)
|
||||
* it is tracking technical debt but the suggested improvements are marginal at best or would require significant research to be evaluated. Prefer having folks who work in the relevant part of the code make improvements based on their judgment.
|
||||
|
||||
|
||||
The following are poor reasons for closing an issue:
|
||||
|
||||
* it has not been updated for a long time. This is fine; if the issue has not changed, then it is normal for it to not be updated.
|
||||
* it is a low-priority user-facing issue. We would prefer to have one long-lived open bug with a single conversation, than many short-lived closed bugs with many independent conversations.
|
||||
* it would be hard to fix.
|
||||
|
||||
|
||||
In general, any bug that has the following characteristics should definitely not be closed:
|
||||
|
||||
* it is a well-described problem that we can reproduce reliably.
|
||||
* it is a well-argued feature request with a solid use case and clear goal that cannot reasonably be implemented in a package. (If it's something we're unlikely to ever do, it should be marked P3.)
|
||||
* it is tracking technical debt that is clearly actionable and whose benefits are clear.
|
||||
* it is a request to add a customization to a material widget that fits cleanly into the existing material design library's ethos.
|
||||
* it was filed by a team member and is assigned to that team member.
|
||||
|
||||
|
||||
## Tracking bugs for team members
|
||||
|
||||
If you need to track some work item, you can file a bug and assign it to yourself. Self-assigned bugs like this are mostly ignored by the bots and you can ignore the rules for such issues. (When you leave the team, we'll likely close these issues.) Some people like to use bugs like this as "umbrella" bugs for tracking work. You may also find it useful to use GitHub projects to manage work items.
|
||||
|
||||
|
||||
## Flaky tests
|
||||
|
||||
When a test flakes, a P0 bug is automatically filed with the label [`team: flakes`](https://github.com/flutter/flutter/issues?q=is%3Aopen+is%3Aissue+label%3A%22team%3A+flakes%22+sort%3Aupdated-asc). This issue should be investigated with all due haste, and a priority level should then be assigned to the issue. At any particular time, the most flaky tests should remain P0. However, flakes that are hard to pin down may be downgraded in priority (e.g. to P1). Please do not ignore the issue entirely, however, and make sure to close bugs once they are resolved, even if it's by magic.
|
||||
|
||||
_See also: [Reducing test flakiness](https://github.com/flutter/flutter/wiki/Reducing-Test-Flakiness)_
|
||||
77
docs/contributing/testing/Flutter-Test-Fonts.md
Normal file
77
docs/contributing/testing/Flutter-Test-Fonts.md
Normal file
@@ -0,0 +1,77 @@
|
||||
Flutter tests run via the `flutter test` command have access to several readily available test fonts, including [FlutterTest](#the-fluttertest-test-font) and [Ahem](https://www.w3.org/Style/CSS/Test/Fonts/Ahem/).
|
||||
|
||||
In tests, if [`fontFamily`](https://master-api.flutter.dev/flutter/painting/TextStyle/fontFamily.html) isn't specified or the specified font families are not available, the default test font `FlutterTest` will be used.
|
||||
If you wish to use a custom font in tests, check out the [`FontLoader`](https://master-api.flutter.dev/flutter/services/FontLoader-class.html) class, and [this example](https://github.com/flutter/flutter/blob/6ec444506375cfa94535a45c2320e01094c295e0/packages/flutter/test/material/icons_test.dart#L149-L172).
|
||||
|
||||
## The `FlutterTest` test font
|
||||
|
||||
### Font Metrics (in design units)
|
||||
|
||||
| | Ascent | Descent | Line Gap (Leading) | Units Per EM | Underline Position |
|
||||
| :-- | :---: | :---: | :---: | :---: | :---: |
|
||||
| `FlutterTest` | 768 (0.75 em) above the baseline | 256 (0.25 em) below the baseline | 0 | 1024 | 146 under the baseline |
|
||||
| `Ahem` | 800 (0.8 em) above the baseline | 200 (0.2 em) below the baseline | 0 | 1000 | 142 under the baseline |
|
||||
|
||||
The `FlutterTest` font's `1024 units-per-em` is a power of 2, making it less likely to introduce precision loss in metrics calculations, when used as a divisor.
|
||||
Thanks to that, the `FlutterTest` font generally provides more precise and font-engine-agnostic font/glyph metrics than `Ahem`.
|
||||
|
||||
**Example**
|
||||
|
||||
You can expect this test to pass on all platforms (currently with the exception of the web HTML renderer):
|
||||
```dart
|
||||
final painter = TextPainter(
|
||||
text: const TextSpan(
|
||||
text: 'text',
|
||||
style: TextStyle(fontSize: 14.0, /* "fontFamily: 'FlutterTest'" is implied */),
|
||||
),
|
||||
textDirection: TextDirection.ltr,
|
||||
textScaleFactor: 1.0,
|
||||
);
|
||||
final lineMetrics = textPainer.computeLineMetrics().first;
|
||||
|
||||
expect(lineMetrics.height, 14.0);
|
||||
expect(lineMetrics.ascent, 10.5); // 0.75em * 14.0pt
|
||||
expect(lineMetrics.descent, 3.5); // 0.25em * 14.0pt
|
||||
// 'text' is 4 glyphs. Most glyphs are as wide as they are tall.
|
||||
expect(lineMetrics.width, 14.0 * 4);
|
||||
```
|
||||
While with the `Ahem` font you would get [slightly different metrics on different platforms](https://github.com/flutter/flutter/issues/62819), since they use different font engines to scale the font.
|
||||
|
||||
### Glyphs
|
||||
|
||||
(images to be added)
|
||||
|
||||
The font covers most types of glyphs defined in the `Ahem` font.
|
||||
|
||||
| Square | Ascent Flushed | Descent Flushed | .notdef |
|
||||
| :---: | :----: | :----: | :----: |
|
||||
| a box that fills the em square | the **Square** glyph but without the part above the baseline | the **Square** glyph but without the part below the baseline | a hollow box |
|
||||
|
||||
The remaining glyphs (for example, **Full Advance**, **1/2 Advance**) are defined with no outlines in the glyph, with different x-advances.
|
||||
|
||||
|
||||
### Glyph Mapping
|
||||
|
||||
Unmapped codepoints will be mapped to the **.notdef** glyph in the test environment.
|
||||
|
||||
| \ Script <br />Glyph | DFLT | grek | hani | latn |
|
||||
| :--- | :----: | :----: | :----: | :----: |
|
||||
| Square | **codepoint(s):** 0x21-0x26, 0x28-0x40, 0x5b-0x60, 0x7b-0x7e, 0xa1-0xa9, 0xab-0xb9, 0xbb-0xbf, 0xd7, 0xf7, 0x2c6-0x2c7, 0x2c9, 0x2d8-0x2dd, 0x2013-0x2014, 0x2018-0x201a, 0x201c-0x201e, 0x2020-0x2022, 0x2026, 0x2030, 0x2039-0x203a, 0x2044, 0x2122, 0x2202, 0x2206, 0x220f, 0x2211-0x2212, 0x2219-0x221a, 0x221e, 0x222b, 0x2248, 0x2260, 0x2264-0x2265, 0x22f2, 0x25ca, 0xf000-0xf002<br />**character(s):** `!` `"` `#` `$` `%` `&` `(` `)` `*` `+` `,` `-` `.` `/` `0` `1` `2` `3` `4` `5` `6` `7` `8` `9` `:` `;` `<` `=` `>` `?` `@` `[` `\` `]` `^` `_` `` ` `` `{` `\|` `}` `~` `¡` `¢` `£` `¤` `¥` `¦` `§` `¨` `©` `«` `¬` `<SOFT HYPHEN>` `®` `¯` `°` `±` `²` `³` `´` `µ` `¶` `·` `¸` `¹` `»` `¼` `½` `¾` `¿` `×` `÷` `ˆ` `ˇ` `ˉ` `˘` `˙` `˚` `˛` `˜` `˝` `–` `—` `‘` `’` `‚` `“` `”` `„` `†` `‡` `•` `…` `‰` `‹` `›` `⁄` `™` `∂` `∆` `∏` `∑` `−` `∙` `√` `∞` `∫` `≈` `≠` `≤` `≥` `⋲` `◊` `<0xf000>` `<0xf001>` `<0xf002>` | **codepoint(s):** 0x394, 0x3a5, 0x3a7, 0x3a9, 0x3bc, 0x3c0, 0x2126<br />**character(s):** `Δ` `Υ` `Χ` `Ω` `μ` `π` `Ω` | **codepoint(s):** 0x3007, 0x4e00, 0x4e03, 0x4e09, 0x4e2d, 0x4e5d, 0x4e8c, 0x4e94, 0x516b, 0x516d, 0x5341, 0x5426, 0x56d7, 0x56db, 0x571f, 0x6587, 0x6587, 0x662f, 0x6728, 0x672c, 0x6b63, 0x6c34, 0x6d4b, 0x706b, 0x786e, 0x8bd5, 0x91d1<br />**character(s):** `〇` `一` `七` `三` `中` `九` `二` `五` `八` `六` `十` `否` `囗` `四` `土` `文` `文` `是` `木` `本` `正` `水` `测` `火` `确` `试` `金` | **codepoint(s):** 0x41-0x5a, 0x61-0x7a, 0xaa, 0xba, 0xc0-0xc8, 0xca-0xd6, 0xd8-0xf6, 0xf8-0xff, 0x131, 0x152-0x153, 0x178, 0x192<br />**character(s):** `A` `B` `C` `D` `E` `F` `G` `H` `I` `J` `K` `L` `M` `N` `O` `P` `Q` `R` `S` `T` `U` `V` `W` `X` `Y` `Z` `a` `b` `c` `d` `e` `f` `g` `h` `i` `j` `k` `l` `m` `n` `o` `p` `q` `r` `s` `t` `u` `v` `w` `x` `y` `z` `ª` `º` `À` `Á` `Â` `Ã` `Ä` `Å` `Æ` `Ç` `È` `Ê` `Ë` `Ì` `Í` `Î` `Ï` `Ð` `Ñ` `Ò` `Ó` `Ô` `Õ` `Ö` `Ø` `Ù` `Ú` `Û` `Ü` `Ý` `Þ` `ß` `à` `á` `â` `ã` `ä` `å` `æ` `ç` `è` `é` `ê` `ë` `ì` `í` `î` `ï` `ð` `ñ` `ò` `ó` `ô` `õ` `ö` `ø` `ù` `ú` `û` `ü` `ý` `þ` `ÿ` `ı` `Œ` `œ` `Ÿ` `ƒ` |
|
||||
| Ascent Flushed | | | | **codepoint(s):** 0x70<br />**character(s):** `p` |
|
||||
| Descent Flushed | | | | **codepoint(s):** 0xc9<br />**character(s):** `É` |
|
||||
| Full Advance | **codepoint(s):** 0x20<br />**character(s):** `<SPACE>` | | | |
|
||||
| 1/2 Advance | **codepoint(s):** 0x2002<br />**character(s):** `<EN SPACE>` | | | |
|
||||
| 1/3 Advance | **codepoint(s):** 0x2004<br />**character(s):** `<THREE-PER-EM SPACE>` | | | |
|
||||
| 1/4 Advance | **codepoint(s):** 0x2005<br />**character(s):** `<FOUR-PER-EM SPACE>` | | | |
|
||||
| 1/6 Advance | **codepoint(s):** 0x2006<br />**character(s):** `<SIX-PER-EM SPACE>` | | | |
|
||||
| 1/5 Advance | **codepoint(s):** 0x2009<br />**character(s):** `<THIN SPACE>` | | | |
|
||||
| 1/10 Advance | **codepoint(s):** 0x200a<br />**character(s):** `<HAIR SPACE>` | | | |
|
||||
| Zero Advance | **codepoint(s):** 0xfeff<br />**character(s):** `<ZERO WIDTH NO-BREAK SPACE>` | | | |
|
||||
|
||||
### Caveats
|
||||
|
||||
To disable FreeType auto-hinter, the family name defined within the font is not `FlutterTest` but `MingLiU`. This typically doesn't affect framework tests as the font is registered under the name `FlutterTest`.
|
||||
|
||||
### Adding more Codepoints/Glyphs to the `FlutterTest` Font
|
||||
|
||||
The `FlutterTest` font is generated by [this script](https://github.com/flutter/engine/blob/170cbea/tools/gen_test_font.py).
|
||||
@@ -0,0 +1,121 @@
|
||||
There are many ways to write a memory test for Flutter. In this article, we give 3 classes of example tests that are currently used by Flutter device lab. Memory performance is a high priority for Flutter so there are many new memory tools and test utilities in progress. We’ll add them in this doc in the future.
|
||||
|
||||
## [MemoryTest][class MemoryTest] that interacts with adb directly
|
||||
|
||||
These memory tests use the [MemoryTest class defined in the device lab perf_tests.dart][class MemoryTest] to poll adb directly before and after an overridable `useMemory` function. By default, `useMemory` will just run an app in release and wait for a “done” message to be printed in logcat.
|
||||
|
||||
Examples include
|
||||
- [`complex_layout_scroll_perf__memory`][complex layout memory manifest]
|
||||
- [device lab task file][complex layout memory task]
|
||||
- [main file][complex layout memory main]
|
||||
- [`fast_scroll_large_images_memory`][fast scroll memory manifest]
|
||||
- [device lab task file][fast scroll memory task]
|
||||
- [main file][fast scroll memory main]
|
||||
|
||||
To write a new MemoryTest case `some_memory_perf` and add it to Flutter’s device lab so Flutter’s CI system can measure it for each Flutter commit, follow examples above to
|
||||
|
||||
1. Create a `main` function for the test app in a file named like `test_memory/some_memory_perf.dart`.
|
||||
2. Add a `some_memory_perf` entry to [manifest.yaml][manifest]
|
||||
3. Add a `some_memory_perf.dart` file to [dev/devicelab/bin/tasks][tasks] folder.
|
||||
|
||||
|
||||
### Pros
|
||||
- Low overhead.
|
||||
- Works in all runtime modes, including release.
|
||||
- The test has complete control on when to start and stop the memory measurement.
|
||||
|
||||
### Cons
|
||||
- Only have 2 memory readings, begin and end, during the app run.
|
||||
- Polling ADB may trigger collections of the Java heap.
|
||||
- Only works on Android targets.
|
||||
- Requires a test environment with access to ADB.
|
||||
- Requires a host machine with Flutter SDK installed.
|
||||
|
||||
## DevTools Memory Test
|
||||
|
||||
The memory tests use DevTools to poll adb and Dart VM during a normal Flutter driver test run, which typically measures speed performance instead of memory performance. [DevToolsMemoryTest][class DevToolsMemoryTest] handles most of the process so a new test only needs to specify the driver test location.
|
||||
|
||||
Examples include
|
||||
- [`complex_layout_scroll_perf__devtools_memory`][complex layout devtools memory manifest]
|
||||
- [device lab task file][complex layout devtools memory task]
|
||||
- [`large_image_changer_perf_android`][large image changer manifest]
|
||||
- [device lab task file][large image changer task]
|
||||
|
||||
To write a new DevTools memory test case `some_memory_perf` and add it to Flutter’s device lab so Flutter’s CI system can measure it for each Flutter commit, follow examples above to
|
||||
|
||||
1. Write (or reuse) a normal Flutter driver test for the app in files named like `test_driver/some_memory_perf.dart` and `test_driver/some_memory_perf_test.dart`.
|
||||
2. Add a `some_memory_perf` entry to [manifest.yaml][manifest]
|
||||
3. Add a `some_memory_perf.dart` file to [dev/devicelab/bin/tasks][tasks] folder.
|
||||
|
||||
### Pros
|
||||
- Have finer grained measurements (~1 reading per second).
|
||||
- Also have Dart VM memory info.
|
||||
- Can easily turn a speed-focused driver test into a memory test.
|
||||
|
||||
### Cons
|
||||
- Don’t have much control on when to start and stop the measurement.
|
||||
- Polling ADB may trigger collections on the Java heap.
|
||||
- Requires a test environment with access to ADB.
|
||||
- Only works on Android targets.
|
||||
- Not available for release mode, so may incur extra memory overhead in profile or debug mode.
|
||||
- Requires a host machine with Flutter SDK installed.
|
||||
|
||||
## iOS Memory Test
|
||||
|
||||
The iOS embedding of Flutter supports sampling memory usage during runtime, which then writes metrics to the [Dart timeline][Dart timeline]. After recording a timeline for the relevant portion of an application’s execution, the timeline can be analyzed to obtain memory related information from the profile.
|
||||
|
||||
Examples include
|
||||
- [`large_image_changer_perf_ios`][large image changer manifest ios]
|
||||
- [device lab task file][large image changer task ios]
|
||||
|
||||
To write a new iOS memory test case `some_memory_perf` and add it to Flutter’s device lab so Flutter’s CI system can measure it for each Flutter commit, follow examples above to
|
||||
|
||||
1. Write (or reuse) a normal Flutter driver test for the app in files named like `test_driver/some_memory_perf.dart` and `test_driver/some_memory_perf_test.dart`.
|
||||
2. Add a `some_memory_perf` entry to [manifest.yaml][manifest]
|
||||
3. Add a `some_memory_perf.dart` file to [dev/devicelab/bin/tasks][tasks] folder that specifies `measureMemory: true`.
|
||||
|
||||
### Pros
|
||||
- Can be run on a machine that does not have the Flutter SDK installed.
|
||||
- Can adjust the sampling frequency so one can have as many or as few measurements as needed.
|
||||
- Each sampling has much less overhead compared to calling adb
|
||||
- Flutter driver tests on iOS get memory measurements for free.
|
||||
|
||||
### Cons
|
||||
- Only works on iOS targets.
|
||||
- Not available for release mode, so may incur extra memory overhead in profile or debug mode.
|
||||
- Memory polling mechanism may incur additional memory overhead.
|
||||
|
||||
|
||||
[manifest]: https://github.com/flutter/flutter/blob/main/dev/devicelab/manifest.yaml
|
||||
|
||||
[tasks]: https://github.com/flutter/flutter/tree/main/dev/devicelab/bin/tasks
|
||||
|
||||
[class MemoryTest]: https://github.com/flutter/flutter/blob/51bb11f7cece47840a9ee6d6d43db97ab16b31df/dev/devicelab/lib/tasks/perf_tests.dart#L941
|
||||
|
||||
[complex layout memory manifest]: https://github.com/flutter/flutter/blob/7e41425d4af21dec7a7ff072a3ec1387859e32c8/dev/devicelab/manifest.yaml#L329
|
||||
|
||||
[complex layout memory task]: https://github.com/flutter/flutter/blob/main/dev/devicelab/bin/tasks/complex_layout_scroll_perf__memory.dart
|
||||
|
||||
[complex layout memory main]: https://github.com/flutter/flutter/blob/main/dev/benchmarks/complex_layout/test_memory/scroll_perf.dart
|
||||
|
||||
[fast scroll memory manifest]: https://github.com/flutter/flutter/blob/7e41425d4af21dec7a7ff072a3ec1387859e32c8/dev/devicelab/manifest.yaml#L837
|
||||
|
||||
[fast scroll memory task]: https://github.com/flutter/flutter/blob/main/dev/devicelab/bin/tasks/fast_scroll_large_images__memory.dart
|
||||
|
||||
[fast scroll memory main]: https://github.com/flutter/flutter/blob/main/dev/benchmarks/macrobenchmarks/test_memory/large_images.dart
|
||||
|
||||
[class DevToolsMemoryTest]: https://github.com/flutter/flutter/blob/7e41425d4af21dec7a7ff072a3ec1387859e32c8/dev/devicelab/lib/tasks/perf_tests.dart#L1138
|
||||
|
||||
[complex layout devtools memory manifest]: https://github.com/flutter/flutter/blob/7e41425d4af21dec7a7ff072a3ec1387859e32c8/dev/devicelab/manifest.yaml#L359
|
||||
|
||||
[complex layout devtools memory task]: https://github.com/flutter/flutter/blob/main/dev/devicelab/bin/tasks/complex_layout_scroll_perf__devtools_memory.dart
|
||||
|
||||
[large image changer manifest]: https://github.com/flutter/flutter/blob/7e41425d4af21dec7a7ff072a3ec1387859e32c8/dev/devicelab/manifest.yaml#L874
|
||||
|
||||
[large image changer task]: https://github.com/flutter/flutter/blob/main/dev/devicelab/bin/tasks/large_image_changer_perf_android.dart
|
||||
|
||||
[Dart timeline]:https://flutter.dev/docs/development/tools/devtools/timeline
|
||||
|
||||
[large image changer manifest ios]: https://github.com/flutter/flutter/blob/7e41425d4af21dec7a7ff072a3ec1387859e32c8/dev/devicelab/manifest.yaml#L880
|
||||
|
||||
[large image changer task ios]: https://github.com/flutter/flutter/blob/main/dev/devicelab/bin/tasks/large_image_changer_perf_ios.dart
|
||||
@@ -0,0 +1,258 @@
|
||||
There are many ways to write a render speed test for Flutter. In this article, we give one example that uses [e2e][] (or [Flutter driver][flutter_driver]), the [dev/benchmarks/macrobenchmarks][macrobenchmarks] app, and the [dev/devicelab][devicelab] to automatically collect metrics for every future Flutter commit and send them to [flutter/cocoon][cocoon].
|
||||
|
||||
The instructions below are for contributors who want to expose a Flutter SDK (framework or engine) performance issue, or write pull requests to fix such issues. If one only needs to test the performance of a particular Flutter app, please reference
|
||||
- https://flutter.dev/docs/cookbook/testing/integration/introduction.
|
||||
- [https://flutter.dev/docs/perf/rendering](https://flutter.dev/docs/perf/rendering)
|
||||
|
||||
Since Flutter Web and Flutter Desktop are still in their early stages, the content here is only well tested and supported on mobile platforms (Android/iOS). We'll come up with docs on how to write performance tests for Web/Desktop later.
|
||||
|
||||
Throughout this doc, we assume that the render speed test is for some `super_important_case`.
|
||||
|
||||
## 1. Add a page to macrobenchmarks
|
||||
|
||||
The [macrobenchmarks][] is a Flutter app that includes many pages each of which corresponds to a specific performance test scenario. It provides some boilerplate code and auto-generated files so when a new scenario needs to be tested, one only needs to add a single page and a handful of files to the [Flutter repo][flutter_repo] instead of adding a new Flutter app with dozens of auto-generated files. (The "macro" means that it's benchmarking a big system, including the whole Flutter framework and engine, instead of just a micro Dart or C++ function.)
|
||||
|
||||
To add a new test scenario `super_important_case`, do the following:
|
||||
|
||||
1. Create a `super_important_case.dart` inside [macrobenchmarks/lib/src][] to define a `SuperImportantCasePage extends StatelessWidget {...}`. If there's a minimal Flutter app with a single `main.dart` file that reproduces the performance issue in the `super_important_case`, we'd often copy the content of that `main.dart` to `super_important_case.dart`.
|
||||
2. Add a `const String kSuperImportantCaseRouteName = '/super_important_case'` to [macrobenchmarks/lib/common.dart][] for later use.
|
||||
3. Open [macrobenchmarks/lib/main.dart][] and add the `kSuperImportantCaseRouteName: (BuildContext conttext) => SuperImportantCasePage(),` to the routes of [`MacrobenchmarksApp`][].
|
||||
4. Scroll down to [`HomePage`'s `ListView`][] and add the following `RaisedButton` so manual testers and the Flutter driver can tap it to navigate to the `super_important_case`.
|
||||
|
||||
```Dart
|
||||
RaisedButton(
|
||||
key: const Key(kSuperImportantCaseRouteName),
|
||||
child: const Text('Super Important Case'),
|
||||
onPressed: () {
|
||||
Navigator.pushNamed(context, kSuperImportantCaseRouteName);
|
||||
},
|
||||
),
|
||||
```
|
||||
|
||||
## 2. Add an e2e test
|
||||
|
||||
When the `super_important_case` page above is finished and manually tested, one can then add an automated integration test to get some performance metrics as follows.
|
||||
|
||||
1. We use [macrobenchmarks/test_driver/e2e_test.dart][] as the host side script. All other tests depends on this file, so discuss with other Flutter members first if you want to change it.
|
||||
|
||||
2. Add `super_important_case_e2e.dart` to [macrobenchmarks/test][] with the following content. The `macroPerfTestE2E` function will navigate the macrobenchmarks app to the `super_important_case` page, and starts collecting performance metrics. The optional arguments are:
|
||||
- The `pageDelay` is the time delay for loading the page. By default it doesn't wait.
|
||||
- The `duration` is the performance metric sampling time.
|
||||
- The `timeout` specifies the backstop timeout implemented by the test package, See [testWidgets](https://api.flutter.dev/flutter/flutter_test/testWidgets.html).
|
||||
- The `body` provides custom ways of driving that page during the benchmark such as scrolling through lists. When this is used together with `duration`, the test will perform for which ever last longer.
|
||||
- The `setup` provides the operation needed to setup before benchmark starts.
|
||||
|
||||
|
||||
```Dart
|
||||
// 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 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:macrobenchmarks/common.dart';
|
||||
|
||||
import 'util.dart';
|
||||
|
||||
void main() {
|
||||
macroPerfTestE2E(
|
||||
'super_important_case',
|
||||
kSuperImportantCaseRouteName,
|
||||
/* optional */ pageDelay: const Duration(seconds: 1),
|
||||
/* optional */ duration: const Duration(seconds: 3),
|
||||
/* optional */ timeout: const Duration(seconds: 30),
|
||||
/* optional */ body: (WidgetController controller) async {
|
||||
...
|
||||
},
|
||||
/* optional */ setup: (WidgetController controller) async {
|
||||
...
|
||||
},
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Once all steps above are done, one should be able to run `flutter drive -t test/super_important_case_perf.dart --driver test_driver/e2e_test.dart` inside the [macrobenchmarks][] directory. After the driver test finished, the metrics should be written into a json file named `e2e_perf_summary.json` inside a temporary `build` directory under the current [macrobenchmarks][] directory.
|
||||
|
||||
|
||||
<!--- TODO explain what these metrics mean in the future -->
|
||||
Some useful metrics in that json file include
|
||||
- `average_frame_build_time_millis`
|
||||
- `average_frame_rasterization_time_millis`
|
||||
- `worst_frame_build_time_millis`
|
||||
- `worst_frame_rasterization_time_millis`
|
||||
|
||||
## 2a. Add a driver test (deprecated)
|
||||
|
||||
(Skip this if step 2 is sufficient for you.)
|
||||
|
||||
When the `super_important_case` page above is finished and manually tested, one can then add an automatic driver test to get some performance metrics as follows.
|
||||
|
||||
1. We use [macrobenchmarks/test_driver/run_app.dart] as the device side app. All other tests depends on this file, so discuss with other Flutter members first if you want to change it.
|
||||
|
||||
2. Add `super_important_case_perf_test.dart` to [macrobenchmarks/test_driver][] with the following content. The `macroPerfTest` function will navigate the macrobenchmarks app to the `super_important_case` page, and starts collecting performance metrics. The `driverOps` provides custom ways of driving that page during the benchmark such as scrolling through lists. The `setupOps` provides the operation needed to setup before benchmark starts.
|
||||
|
||||
```Dart
|
||||
import 'package:flutter_driver/flutter_driver.dart';
|
||||
import 'package:macrobenchmarks/common.dart';
|
||||
|
||||
import 'util.dart';
|
||||
|
||||
void main() {
|
||||
macroPerfTest(
|
||||
'super_important_case',
|
||||
kSuperImportantCaseRouteName,
|
||||
pageDelay: const Duration(seconds: 1),
|
||||
/* optional */ driverOps: (FlutterDriver driver) async {
|
||||
...
|
||||
},
|
||||
/* optional */ setupOps: (FlutterDriver driver) async {
|
||||
...
|
||||
},
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Once all steps above are done, one should be able to run `flutter drive -t test_driver/run_app.dart --driver test_driver/super_important_case_perf.dart` inside the [macrobenchmarks][] directory. After the driver test finished, the metrics should be written into a json file named `super_important_case_perf__timeline_summary.json` inside a temporary `build` directory under the current [macrobenchmarks][] directory.
|
||||
|
||||
|
||||
<!--- TODO explain what these metrics mean in the future -->
|
||||
Some useful metrics in that json file include
|
||||
- `average_frame_build_time_millis`
|
||||
- `average_frame_rasterization_time_millis`
|
||||
- `worst_frame_build_time_millis`
|
||||
- `worst_frame_rasterization_time_millis`
|
||||
|
||||
## 3. Update README
|
||||
|
||||
Add the new test to the list in [macrobenchmarks/README.md].
|
||||
|
||||
## 4. Add a task to devicelab
|
||||
|
||||
To keep Flutter performant, running a test locally once in a while and check the metrics manually is insufficient. The following steps let the [devicelab][] run the test automatically for every Flutter commit so performance regressions or speedups for the `super_important_case` can be detected quickly.
|
||||
|
||||
1. Add `super_important_case_perf__e2e_summary` to [dev/devicelab/manifest.yaml][] under `tasks`. Follow other tasks to properly set descriptions and choose agent such as `linux/android` (Moto G4) or `mac/ios` (iPhone 6s). Mark it `flaky: true` so that while we observe the test case behavior on devicelab, we don't block the build tree.
|
||||
|
||||
2. Add `super_important_case_perf__e2e_summary.dart` to [dev/devicelab/bin/tasks][] with a content like
|
||||
|
||||
```Dart
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter_devicelab/tasks/perf_tests.dart';
|
||||
import 'package:flutter_devicelab/framework/adb.dart';
|
||||
import 'package:flutter_devicelab/framework/framework.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
deviceOperatingSystem = DeviceOperatingSystem.android; // or ios
|
||||
await task(createSuperImportantCasePerfE2ETest());
|
||||
}
|
||||
```
|
||||
|
||||
3. Add the following `createSuperImportantCasePerfTest` function to [dev/devicelab/lib/tasks/perf_tests.dart][]
|
||||
|
||||
```Dart
|
||||
TaskFunction createSuperImportantCasePerfE2ETest() {
|
||||
return PerfTest.e2e(
|
||||
'${flutterDirectory.path}/dev/benchmarks/macrobenchmarks',
|
||||
'test/super_important_case_e2e.dart',
|
||||
).run;
|
||||
}
|
||||
```
|
||||
|
||||
4. Locally test the devicelab task by running `../../bin/cache/dart-sdk/bin/dart bin/run.dart -t super_important_case_perf__e2e_summary` inside the [dev/devicelab][devicelab] directory with an Android or iOS device connected. You should see a success and a summary of metrics being printed out.
|
||||
|
||||
5. Submit a pull request of everything above.
|
||||
|
||||
6. Finally, remove `flaky: true` once the test is proven to be reliable for a few days. Since this may take a while, creating a reminder calendar event could be a good idea.
|
||||
|
||||
## 4a. Add a task to devicelab for driver tests (deprecated)
|
||||
|
||||
(Skip this if you didn't do step 2a.)
|
||||
|
||||
To keep Flutter performant, running a test locally once in a while and check the metrics manually is insufficient. The following steps let the [devicelab][] run the test automatically for every Flutter commit so performance regressions or speedups for the `super_important_case` can be detected quickly.
|
||||
|
||||
1. Add `super_important_case_perf__timeline_summary` to [dev/devicelab/manifest.yaml][] under `tasks`. Follow other tasks to properly set descriptions and choose agent such as `linux/android` (Moto G4) or `mac/ios` (iPhone 6s).
|
||||
|
||||
2. Add `super_important_case_perf__timeline_summary.dart` to [dev/devicelab/bin/tasks][] with a content like
|
||||
|
||||
```Dart
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter_devicelab/tasks/perf_tests.dart';
|
||||
import 'package:flutter_devicelab/framework/adb.dart';
|
||||
import 'package:flutter_devicelab/framework/framework.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
deviceOperatingSystem = DeviceOperatingSystem.android; // or ios
|
||||
await task(createSuperImportantCasePerfTest());
|
||||
}
|
||||
```
|
||||
|
||||
3. Add the following `createSuperImportantCasePerfTest` function to [dev/devicelab/lib/tasks/perf_tests.dart][]
|
||||
|
||||
```Dart
|
||||
TaskFunction createSuperImportantCasePerfTest() {
|
||||
return PerfTest(
|
||||
'${flutterDirectory.path}/dev/benchmarks/macrobenchmarks',
|
||||
'test_driver/run_app.dart',
|
||||
'super_important_case_perf',
|
||||
testDriver: 'test_driver/super_important_case_perf_test.dart',
|
||||
).run;
|
||||
}
|
||||
```
|
||||
|
||||
4. Locally test the devicelab task by running `../../bin/cache/dart-sdk/bin/dart bin/run.dart -t super_important_case_perf__timeline_summary` inside the [dev/devicelab][devicelab] directory with an Android or iOS device connected. You should see a success and a summary of metrics being printed out.
|
||||
|
||||
5. Submit a pull request of everything above.
|
||||
|
||||
6. Finally, remove `flaky: true` once the test is proven to be reliable for a few days. Since this may take a while, creating a reminder calendar event could be a good idea.
|
||||
|
||||
## 5. Set benchmark baseline
|
||||
|
||||
Tasks will be run automatically in the [devicelab], and the result is shown in [flutter-dashboard]. Set the baseline in [flutter-dashboard] once the new test gets enough data. Also for metrics like "vsync_transitions_missed", change the unit from default ms to frames or other suitable units.
|
||||
|
||||
## Acknowledgement
|
||||
|
||||
Big congratulations if you've successfully finished all steps above! You just made a big contribution to Flutter's performance. Please also feel encouraged to improve this doc to help future contributors (which probably include a future yourself that would forget something above in a few months)!
|
||||
|
||||
<!-- Reference links below -->
|
||||
[flutter_driver]: https://github.com/flutter/flutter/tree/main/packages/flutter_driver
|
||||
|
||||
[macrobenchmarks]: https://github.com/flutter/flutter/tree/main/dev/benchmarks/macrobenchmarks
|
||||
|
||||
[cocoon]: https://github.com/flutter/cocoon
|
||||
|
||||
[devicelab]: https://github.com/flutter/flutter/tree/main/dev/devicelab
|
||||
|
||||
[dev/devicelab/manifest.yaml]: https://github.com/flutter/flutter/tree/main/dev/devicelab/manifest.yaml
|
||||
|
||||
[flutter_repo]: https://github.com/flutter/flutter
|
||||
|
||||
[macrobenchmarks/lib/src]: https://github.com/flutter/flutter/tree/main/dev/benchmarks/macrobenchmarks/lib/src
|
||||
|
||||
[macrobenchmarks/lib/common.dart]: https://github.com/flutter/flutter/tree/main/dev/benchmarks/macrobenchmarks/lib/common.dart
|
||||
|
||||
[macrobenchmarks/lib/main.dart]: https://github.com/flutter/flutter/tree/main/dev/benchmarks/macrobenchmarks/lib/main.dart
|
||||
|
||||
[`MacrobenchmarksApp`]: https://github.com/flutter/flutter/blob/94b7ff241e6e5445b7f30215a777eb4971311797/dev/benchmarks/macrobenchmarks/lib/main.dart#L24
|
||||
|
||||
[`HomePage`'s `ListView`]: https://github.com/flutter/flutter/blob/94b7ff241e6e5445b7f30215a777eb4971311797/dev/benchmarks/macrobenchmarks/lib/main.dart#L58
|
||||
|
||||
[macrobenchmarks/test_driver]: https://github.com/flutter/flutter/tree/main/dev/benchmarks/macrobenchmarks/test_driver
|
||||
|
||||
[macrobenchmarks/test_driver/run_app.dart]: https://github.com/flutter/flutter/tree/main/dev/benchmarks/macrobenchmarks/test_driver/run_app.dart
|
||||
|
||||
[macrobenchmarks/README.md]: https://github.com/flutter/flutter/blob/main/dev/benchmarks/macrobenchmarks/README.md
|
||||
|
||||
[dev/devicelab/bin/tasks]: https://github.com/flutter/flutter/tree/main/dev/devicelab/bin/tasks
|
||||
|
||||
[dev/devicelab/lib/tasks/perf_tests.dart]: https://github.com/flutter/flutter/tree/main/dev/devicelab/lib/tasks/perf_tests.dart
|
||||
|
||||
[flutter-dashboard]: https://flutter-dashboard.appspot.com/benchmarks.html
|
||||
|
||||
[e2e]: https://pub.dev/packages/e2e
|
||||
|
||||
[macrobenchmarks/test_driver/e2e_test.dart]: https://github.com/flutter/flutter/blob/main/dev/benchmarks/macrobenchmarks/test_driver/e2e_test.dart
|
||||
|
||||
[macrobenchmarks/test]: https://github.com/flutter/flutter/blob/main/dev/benchmarks/macrobenchmarks/test
|
||||
@@ -0,0 +1,108 @@
|
||||
In the last months Flutter Driver tests has been enabled for most browsers.
|
||||
|
||||
This document explains the steps that should be followed before running the tests, how to run the tests, example usages and handy tools that can be used.
|
||||
|
||||
## Preparing the driver
|
||||
|
||||
The first step of using Flutter Driver tests for Flutter Web testing is to install(prepare) the driver for the target browser.
|
||||
|
||||
### Using Chrome
|
||||
|
||||
For Chrome Desktop browsers:
|
||||
|
||||
- Check the version of Chrome.
|
||||
- Download the Chrome driver for that version from [driver downloads](https://chromedriver.chromium.org/downloads).
|
||||
- Start the driver on port 4444. `chromedriver --port=4444`
|
||||
|
||||
Chrome on Android browser tests can be run both on device and on the emulator.
|
||||
|
||||
- Both for using a real device and emulator, make sure that Android platform tools are installed
|
||||
- For using an emulator, follow the [instructions](https://developer.android.com/studio/run/managing-avds) for creating and managing one.
|
||||
- For real device tests check the devices Chrome's version. Please note that the Chrome installed on the emulator will probably have a different version than the host machine. Check the emulator's Chrome's version.
|
||||
- Download the driver [driver downloads](https://chromedriver.chromium.org/downloads).
|
||||
- Start the adb server: `adb start-server` you can later kill with `adb kill-server`
|
||||
- For the web port you are planning to use for Flutter driver tests, let's say 8080 for example:
|
||||
- Test the browser has access to a server running on localhost:8080
|
||||
- One can utilize adb for this purpose. For more [details](https://developer.android.com/studio/command-line/adb).
|
||||
- Another alternative is using Chrome Remote devices from browser page `chrome://inspect/devices#devices`. For more details on useful links: [remote debugging webviews](https://developers.google.com/web/tools/chrome-devtools/remote-debugging/webviews), [remote debugging android devices](https://developers.google.com/web/tools/chrome-devtools/remote-debugging)
|
||||
- Start the Chrome driver on port 4444. `chromedriver --port=4444`
|
||||
|
||||
### Using Safari
|
||||
|
||||
Like Safari browser Safari driver also comes installed on the macOS devices. For using Safari on macOS steps are easy:
|
||||
|
||||
- Use the [instructions](https://developer.apple.com/documentation/webkit/testing_with_webdriver_in_safari) to enable safari driver.
|
||||
- start safari driver on port 4444 `./usr/bin/safaridriver --port=4444`
|
||||
|
||||
IOS Safari can be run on a simulator. Simulators are part of Xcode, more [details](https://developer.apple.com/documentation/xcode). After making sure your macOS have simulators, follow the steps above to start the Safari driver. Unlike Android the Desktop Safari version and simulator version is the same.
|
||||
|
||||
### Using Firefox
|
||||
|
||||
- Check the version of Firefox.
|
||||
- Download the Gecko driver for that version from [the releases](https://github.com/mozilla/geckodriver/releases).
|
||||
- Add the Firefox driver to your path.
|
||||
|
||||
Note that this section is experimental, at this point we don't have automated tests running on Firefox.
|
||||
|
||||
### Using Edge
|
||||
|
||||
More information can be found on Edge Drivers on [developer site](https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/). Edge driver should also be added to the path after installation.
|
||||
|
||||
Note that this section is experimental, at this point we don't have automated tests running on Edge.
|
||||
|
||||
## Running Flutter Driver tests
|
||||
|
||||
The command for running the driver tests:
|
||||
|
||||
```
|
||||
flutter drive --target=test_driver/[driver_test].dart -d web-server --release --browser-name=chrome --web-port=8080
|
||||
```
|
||||
|
||||
Let's go over the different arguments that can be used:
|
||||
|
||||
- Use one of the six browsers for `--browser-name` parameter: chrome, safari, ios-safari, android-chrome, firefox, edge.
|
||||
- Use `--local-engine=host_debug_unopt --local-engine-host=host_debug_unopt` for running tests with a local engine.
|
||||
- Use `--release` or `--profile` mode for running the tests. Debug mode will be supported soon.
|
||||
- Change the `--webport` as needed, don't forget to change remote debugging settings for Android Chrome.
|
||||
- Use `--no-android-emulator` for using Android with real devices.
|
||||
|
||||
## Web Installers Repo
|
||||
|
||||
Web installers is a new Flutter project [repository](https://github.com/flutter/web_installers) where we are planning to add utilities for launching, downloading browsers/drivers.
|
||||
|
||||
Currently it can be used for downloading/running Chrome Driver:
|
||||
|
||||
```
|
||||
dart lib/web_driver_installer.dart chromedriver --driver-version="78.0.3904.105"
|
||||
```
|
||||
|
||||
Or for running the Safari driver:
|
||||
|
||||
```
|
||||
dart lib/web_driver_installer.dart safaridriver
|
||||
```
|
||||
|
||||
For more details use the [documentation](https://github.com/flutter/web_installers/tree/master/packages/web_drivers).
|
||||
|
||||
## Examples From Flutter Project
|
||||
|
||||
We already use Flutter Driver in many different places in Flutter Project. We have a smoke test running as a [Cirrus CI task](https://github.com/flutter/flutter/blob/main/.cirrus.yml#L291) in Flutter repo, which is also a great example for showing web_installers + flutter drive usage.
|
||||
|
||||
```
|
||||
script:
|
||||
- flutter config --enable-web
|
||||
- git clone https://github.com/flutter/web_installers.git
|
||||
- cd web_installers/packages/web_drivers/
|
||||
- pub get
|
||||
- dart lib/web_driver_installer.dart &
|
||||
- sleep 20
|
||||
- chromedriver/chromedriver --port=4444 &
|
||||
- sleep 5
|
||||
- cd ../../../examples/hello_world/
|
||||
- flutter drive --target=test_driver/smoke_web_engine.dart -d web-server --profile --browser-name=chrome
|
||||
```
|
||||
|
||||
Other example usages:
|
||||
|
||||
- e2e tests under flutter/packages repo. ([PR](https://github.com/flutter/plugins/pull/2554))
|
||||
- web engine integration tests under engine repo. ([PR](https://github.com/flutter/engine/pull/16930))
|
||||
125
docs/contributing/testing/Running-and-writing-tests.md
Normal file
125
docs/contributing/testing/Running-and-writing-tests.md
Normal file
@@ -0,0 +1,125 @@
|
||||
## For the framework
|
||||
|
||||
Dart tests are written using the `flutter_test` package's API,
|
||||
named with the suffix `_test.dart`, and placed inside the
|
||||
`test/` subdirectory of the package under test.
|
||||
|
||||
We support several kinds of tests:
|
||||
|
||||
- Unit tests, e.g. using [`flutter_test`](https://api.flutter.dev/flutter/flutter_test/flutter_test-library.html). See below.
|
||||
|
||||
- Unit tests that use golden-file testing, comparing pixels.
|
||||
See [Writing a golden-file test for package:flutter](Writing-a-golden-file-test-for-package-flutter.md).
|
||||
|
||||
- End-to-end tests, e.g. using [`flutter_driver`](https://api.flutter.dev/flutter/flutter_driver/flutter_driver-library.html) and our [device lab](https://github.com/flutter/flutter/blob/main/dev/devicelab/README.md).
|
||||
|
||||
Our bots run on our [test and build infrastructure](https://github.com/flutter/flutter/blob/main/dev/bots/README.md).
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Flutter tests use the `flutter_test` package ([source](https://github.com/flutter/flutter/tree/main/packages/flutter_test), [API documentation](https://api.flutter.dev/flutter/flutter_test/flutter_test-library.html)),
|
||||
which provides flutter-specific extensions on top of the [Dart `test` package](https://pub.dartlang.org/packages/test).
|
||||
|
||||
To automatically find all files named `*_test.dart` inside a package's `test/` subdirectory, and
|
||||
run them inside the headless flutter shell as a test, use the `flutter test` command, e.g:
|
||||
|
||||
- `cd examples/hello_world`
|
||||
- `flutter test`
|
||||
|
||||
Individual tests can also be run directly, e.g.: `flutter test lib/my_app_test.dart`
|
||||
|
||||
You can view these tests on a device by running them directly using `flutter run`.
|
||||
For tests inside the `packages/flutter` directory, you will need to copy them to
|
||||
(or symlink to them from) the `test/` directory of an actual app (e.g. the flutter
|
||||
gallery), since the `flutter` package itself is not set up to execute as an
|
||||
application (which is necessary to use `flutter run` with a test).
|
||||
|
||||
Unit tests run with `flutter test` run inside a headless flutter shell on your workstation,
|
||||
you won't see any UI. You can use `print` to generate console output or you can interact
|
||||
with the Dart VM via the Dart Observatory at [http://localhost:8181/](http://localhost:8181/).
|
||||
|
||||
To debug tests in Observatory, use the `--start-paused` option to start the test in a
|
||||
paused state and wait for connection from a debugger. This option lets you set breakpoints
|
||||
before the test runs.
|
||||
|
||||
To run analysis and all the tests for the entire Flutter repository, the same way that Cirrus
|
||||
runs them, run `dart dev/bots/test.dart` and `dart --enable-asserts dev/bots/analyze.dart`.
|
||||
|
||||
If you've built your own flutter engine (see [Setting up the Engine development environment](../../engine/dev/Setting-up-the-Engine-development-environment.md)), you
|
||||
can pass `--local-engine` to change what flutter shell `flutter test` uses. For example,
|
||||
if you built an engine in the `out/host_debug_unopt` directory, you can use:
|
||||
|
||||
```
|
||||
flutter test \
|
||||
--local-engine=host_debug_unopt \
|
||||
--local-engine-host=host_debug_unopt
|
||||
```
|
||||
|
||||
to run the tests in the locally built engine. Note that in this case you need to specify `host_debug_unopt`
|
||||
as both arguments.
|
||||
|
||||
To learn how to see how well tested the codebase is, see [Test coverage for package:flutter](Test-coverage-for-package-flutter.md).
|
||||
|
||||
_See also: [Flutter Test Fonts](Flutter-Test-Fonts.md)_
|
||||
|
||||
## Running device lab tests locally
|
||||
|
||||
Flutter runs a number of end-to-end tests in a device lab. The Flutter repo contains code for bootstrapping and executing these tests, in addition to the tests themselves.
|
||||
|
||||
The code that runs the device lab end-to-end tests can be found here:
|
||||
|
||||
```
|
||||
dev/devicelab
|
||||
```
|
||||
|
||||
The tests that run in the device lab can be found here:
|
||||
|
||||
```
|
||||
dev/integration_tests
|
||||
```
|
||||
|
||||
When a device lab test fails, it is important to be able to run the test locally to verify the problem and intended solution. To execute a device lab test locally, do the following:
|
||||
|
||||
1. Navigate in your terminal to the `dev/devicelab` directory.
|
||||
1. Ensure that a physical device, simulator, or emulator is connected.
|
||||
1. Ensure that the current locale is en_US by executing the command: `export LANG=en_US.UTF-8`.
|
||||
1. Execute the command: `../../bin/dart bin/run.dart -t [task_name]` where `[task_name]` is replaced by the name of the task you want to run as defined within `.ci.yaml`.
|
||||
|
||||
### Device lab tests with a local engine
|
||||
|
||||
Sometimes a device lab test fails due to engine changes that you've made. In these cases, you'd like to run the impacted device lab tests locally with your local version of the engine. To do this, pass the appropriate flags to `run.dart`:
|
||||
|
||||
```shell
|
||||
../../bin/dart bin/run.dart \
|
||||
--local-engine-src-path=[path_to_src] \
|
||||
--local-engine=[engine_build_for_your_device] \
|
||||
--local-engine-host=[host_engine_build_for_your_device] \
|
||||
-t [task_name]
|
||||
```
|
||||
|
||||
If your local Flutter engine is in the same directory as your `flutter/` directory then you can omit the `--local-engine-src-path` parameter because it will be resolved automatically:
|
||||
|
||||
```
|
||||
../../bin/dart bin/run.dart \
|
||||
--local-engine=[engine_build_for_your_device] \
|
||||
--local-engine-host=[host_engine_build_for_your_device] \
|
||||
-t [task_name]
|
||||
```
|
||||
|
||||
The following is an example of what running the local engine command might look like:
|
||||
|
||||
```
|
||||
../../bin/dart bin/run.dart \
|
||||
--local-engine-src-path=/Users/myname/flutter/engine/src \
|
||||
--local-engine=android_debug_unopt_x86 \
|
||||
--local-engine-host=host_debug_unopt_x86 \
|
||||
-t external_ui_integration_test
|
||||
```
|
||||
|
||||
The above command would use the local Flutter engine located at `/Users/myname/flutter/engine` to execute the `external_ui_integration_test` test on an Android emulator, which is why the `android_debug_unopt_x86` version of the engine is used.
|
||||
|
||||
Note that some tests may require `profile` mode instead of `debug` mode when running with local engine. Make sure to pass in the correct local engine. See [Compiling the engine](../../engine/dev/Compiling-the-engine.md) for more details.
|
||||
|
||||
## For the engine
|
||||
|
||||
See the [Testing the engine](../../engine/testing/Testing-the-engine.md) wiki.
|
||||
@@ -227,4 +227,4 @@ When writing tests, think about the developer who will read this test 6 months f
|
||||
|
||||
# See also
|
||||
|
||||
- [Flutter Test Fonts](https://github.com/flutter/flutter/wiki/Flutter-Test-Fonts)
|
||||
- [Flutter Test Fonts](Flutter-Test-Fonts.md)
|
||||
|
||||
@@ -5,16 +5,14 @@ _(This page is referenced by comments in the Flutter codebase.)_
|
||||
Golden file tests for `package:flutter` use [Flutter Gold](https://flutter-gold.skia.org/?query=source_type%3Dflutter) for baseline and version management of golden files. This allows for golden file testing on Linux, Windows, MacOS and Web, which accounts for the occasional subtle rendering differences between these platforms.
|
||||
|
||||
## Index
|
||||
- [Known Issues](https://github.com/flutter/flutter/wiki/Writing-a-golden-file-test-for-package:flutter#known-issues)
|
||||
- [Build Breakage](https://github.com/flutter/flutter/wiki/Writing-a-golden-file-test-for-package:flutter#build-breakage)
|
||||
- [Creating a New Golden File Test](https://github.com/flutter/flutter/wiki/Writing-a-golden-file-test-for-package%3Aflutter#creating-a-new-golden-file-test)
|
||||
- [Updating a Golden File Test](https://github.com/flutter/flutter/wiki/Writing-a-golden-file-test-for-package%3Aflutter#updating-a-golden-file-test
|
||||
)
|
||||
- [Flutter Gold Login](https://github.com/flutter/flutter/wiki/Writing-a-golden-file-test-for-package%3Aflutter#flutter-gold-login
|
||||
)
|
||||
- [`flutter-gold` Check](https://github.com/flutter/flutter/wiki/Writing-a-golden-file-test-for-package:flutter#flutter-gold-check)
|
||||
- [`reduced-test-set` tag](https://github.com/flutter/flutter/wiki/Writing-a-golden-file-test-for-package:flutter#reduced-test-set-tag)
|
||||
- [Troubleshooting](https://github.com/flutter/flutter/wiki/Writing-a-golden-file-test-for-package:flutter#troubleshooting)
|
||||
- [Known Issues](#known-issues)
|
||||
- [Build Breakage](#build-breakage)
|
||||
- [Creating a New Golden File Test](#creating-a-new-golden-file-test)
|
||||
- [Updating a Golden File Test](#updating-a-golden-file-test)
|
||||
- [Flutter Gold Login](#flutter-gold-login)
|
||||
- [`flutter-gold` Check](#flutter-gold-check)
|
||||
- [`reduced-test-set` tag](#reduced-test-set-tag)
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
|
||||
|
||||
## Known Issues
|
||||
@@ -30,6 +28,8 @@ If you would like to instantly invalidate all prior renderings, changing the nam
|
||||
|
||||
If the Flutter build is broken due to a golden file test failure, this typically means an image change has landed without being triaged. Golden file images should be triaged in pre-submit before a change lands (as described in the steps below). If this process is not followed, a test with an unapproved golden file image will fail in post-submit testing. This will present in the following error message:
|
||||
|
||||
<!-- TODO(Piinks): Update this error message in the framework. -->
|
||||
|
||||
```
|
||||
Skia Gold received an unapproved image in post-submit
|
||||
testing. Golden file images in flutter/flutter are triaged
|
||||
@@ -91,7 +91,7 @@ And that’s it! Your new golden file(s) will be checked in as the baseline(s) f
|
||||
|
||||
## Flutter Gold Login
|
||||
|
||||
Triage permission is currently restricted to members of *flutter-hackers*. For more information, see [Contributor Access](https://github.com/flutter/flutter/wiki/Contributor-access).
|
||||
Triage permission is currently restricted to members of *flutter-hackers*. For more information, see [Contributor Access](../Contributor-access.md).
|
||||
Once you have been added as an authorized user for Flutter Gold, you can log in through the [homepage of the Flutter Gold dashboard](https://flutter-gold.skia.org/) and proceed to triage your image results under [Changelists](https://flutter-gold.skia.org/changelists).
|
||||
|
||||
## `flutter gold` Check
|
||||
|
||||
Reference in New Issue
Block a user