blob: 20271b12cdcdce315648450384665ad531e73cac [file] [log] [blame] [view]
# Fuchsia Networking Contributor Guide
Fuchsia Networking welcomes contributions from all. This document defines
contribution guidelines where they differ from or refine on the guidelines that
apply to Fuchsia as a whole.
## Getting Started
Consult the [getting started document][getting_started] to set up your
development environment.
## Contributor Workflow
Consult the [contribute changes document][contribute_changes] for general
contribution guidance and project-wide best practices. The remainder of this
document describes best practices specific to Fuchsia Networking.
## Coding Guidelines
### Philosophy
This section is inspired by [Flutter's style guide][flutter_philosophy], which
contains many general principles that you should apply to all your programming
work. Read it. The below calls out specific aspects that we feel are
particularly important.
#### Be Lazy
Do not implement features you don't need. It is hard to correctly design unused
code. This is closely related to the commit sizing advice given above; adding a
new data structure to be used in some future commit is akin to adding a feature
you don't need - it is exceedingly hard for your code reviewer to determine if
you've designed the structure correctly because they (and you!) can't see how it
is to be used.
#### Go Down the Rabbit Hole
You will occasionally encounter behaviour that surprises you or seems wrong. It
probably is! Invest the time to find the root cause - you will either learn
something, or fix something, and both are worth your time. Do not work around
behaviour you don't understand.
### Avoid Duplication
Avoid duplicating code whenever possible. In cases where existing code is not
exposed in a manner suitable to your needs, prefer to extract the necessary
parts into a common dependency.
### Error Handling
Avoid unhandled errors and APIs which inherently disallow proper error handling;
for a common example, consider [`fuchsia_async::executor::spawn`][spawn].
`spawn` inherently precludes error passing (since the flow of execution is
severed). In most cases `spawn` can be replaced with a future that is later
included in a [`select`][select] expression ([example commit][spawn_select]) or
simply `await`ed on directly ([example commit][spawn_await]).
### Compile-time over Run-time
Prefer type safety over runtime invariant checking. In other words, arrange your
abstractions such that they cannot express invalid conditions rather than
relying on assertions at runtime.
Write testable code; testable code is modular and its dependencies are easily
injected.
Avoid [magic numbers][magic_number].
### Comments
When writing comments, take a moment to consider the future reader of your
comment. Ensure that your comments are complete sentences with proper grammar
and punctuation. Note that adding more comments or more verbose comments is not
always better; for example, avoid comments that repeat the code they're anchored
on.
Documentation comments should be self-contained; in other words, do not assume
that the reader is aware of documentation in adjacent files or on adjacent
structures. Avoid documentation comments on types which describe _instances_ of
the type; for example, `AddressSet is a set of client addresses.` is a comment
that describes a field of type `AddressSet`, but the type may be used to hold
any kind of `Address`, not just a client's.
Phrase your comments to avoid references that might become stale; for example:
do not mention a variable or type by name when possible (certain doc comments
are necessary exceptions). Also avoid references to past or future versions of
or past or future work surrounding the item being documented; explain things
from first principles rather than making external references (including past
revisions).
When writing TODOs:
1. Include an issue reference using the format `TODO(https://fxbug.dev/42075402):`
1. Phrase the text as an action that is to be taken; it should be possible for
another contributor to pick up the TODO without consulting any external
sources, including the referenced issue.
#### Provide citations
When an implementation is following some specification/document (e.g. RFCs),
include a comment with both a quote and citation of the relevant portion(s) of
the document near the implementation. The quote lets readers know why something
is being done and the citation allows a reader to get more context.
### Error Messages
As with code comments, consider the future reader of the error messages emitted
by your code. Ensure that your error messages are actionable. For example, avoid
test failure messages such as "unexpected value" - always include the unexpected
value; another example is "expected `<variable>` to be empty, was non-empty" -
this message would be much more useful if it included the unexpected elements.
Always consider: what will the reader do with this message?
### Tests
Consult the [testability rubrics][testability_rubrics] for general guidelines on
test-writing and testability reviews on Fuchsia. In Fuchsia Networking, we
define the following test classes:
- **Unit tests** are fully local to a piece of code and all their external
dependencies are faked or mocked.
- **Integration tests** validate behavior between two or more different
components.
- **End-to-end tests** are driven by an external host machine and use the public
APIs and bytes written to the network to perform behavior validation. Can be
performed over a physical network or by virtualization of the DUT (`qemu`).
Consider the following guidelines when writing tests:
1. **Always add tests** for new features or bug fixes.
1. Consider the guidelines in [Error Messages](#Error-Messages) when writing
test assertions.
1. Tests must be **deterministic**. Threaded or time-dependent code, Random
Number Generators (RNGs), and cross-component communication are common
sources of nondeterminism. See [Write reproducible, deterministic tests][determinism]
for tips.
1. **Avoid** tests with **hard-coded timeouts**. Prefer relying on the
framework/fixture to time out tests.
1. Prefer **hermetic tests**; test set-up routines should be explicit and
deterministic. Be mindful of test fixtures that run cases in parallel (such
as Rust's) when using "ambient" services. Prefer to **explicitly inject
component dependencies** that are vital to the test.
1. [Tests should always be components][tests_as_components].
1. Prefer **virtual devices and networks** for non-end-to-end tests. See
[netemul] for guidance on virtual network environments.
1. Avoid [change detector tests][change_detector_tests]; tests that are
unnecessarily sensitive to changes, especially ones external to the code
under test, can hamper feature development and refactoring.
1. Do not encode implementation details in tests, prefer testing through a
module's public API.
1. When unwrapping a `Result<_, fidl::Error>` returned from a FIDL method call,
restate the function being called in the panic message to make it easier to
track down the callsite. Don't repeat the type of the error, which is already
included in the panic output. For example:
```rust
// Bad:
let foo_result = proxy
.foo() // `foo` returns a `Result<_, fidl::Error>`.
.await
.expect("FIDL error"); // Doesn't provide any new information.
// Good:
let foo_result = proxy
.foo() // `foo` returns a `Result<_, fidl::Error>`.
.await
.expect("calling foo"); // Restate the function being called.
```
### Source Control Best Practices
Commits should be arranged for ease of reading; that is, incidental changes
such as code movement or formatting changes should be committed separately from
actual code changes.
Commits should always be focused. For example, a commit could add a feature,
fix a bug, or refactor code, but not a mixture.
Commits should be thoughtfully sized; avoid overly large or complex commits
which can be logically separated, but also avoid overly separated commits that
require code reviews to load multiple commits into their mental working memory
in order to properly understand how the various pieces fit together. **If your
changes require multiple commits, consider whether those changes warrant a
design doc or [RFC][rfc_process]**.
#### Commit Messages
Commit messages should be _concise_ but self-contained (avoid relying on issue
references as explanations for changes) and written such that they are helpful
to people reading in the future (include rationale and any necessary context).
Avoid superfluous details or narrative.
Commit messages should consist of a brief subject line and a separate
explanatory paragraph in accordance with the following:
1. [Separate subject from body with a blank line](https://chris.beams.io/posts/git-commit/#separate)
1. [Limit the subject line to 50 characters](https://chris.beams.io/posts/git-commit/#limit-50)
1. [Capitalize the subject line](https://chris.beams.io/posts/git-commit/#capitalize)
1. [Do not end the subject line with a period](https://chris.beams.io/posts/git-commit/#end)
1. [Use the imperative mood in the subject line](https://chris.beams.io/posts/git-commit/#imperative)
1. [Wrap the body at 72 characters](https://chris.beams.io/posts/git-commit/#wrap-72)
1. [Use the body to explain what and why vs. how](https://chris.beams.io/posts/git-commit/#why-not-how)
The body may be omitted if the subject is self-explanatory; e.g. when fixing a
typo. The git book contains a [Commit Guidelines][commit_guidelines] section
with much of the same advice, and the list above is part of a [blog
post](https://chris.beams.io/posts/git-commit/) by [Chris
Beams](https://chris.beams.io/).
Commit messages should make use of issue tracker integration. See [Commit message style guide](/docs/contribute/commit-message-style-guide.md).
When using issue tracker integration, don't omit necessary context that may
also be included in the relevant issue (commit messages should be
_concise_ but self-contained). Many issues are Google-internal, and any
given issue tracker is not guaranteed to be usable at the time that the commit
history is read.
Commit messages should never contain references to any of:
1. Relative moments in time
1. Non-public URLs
1. Individuals
1. Hosted code reviews (such as on fuchsia-review.googlesource.com)
+ Refer to commits in this repository by their SHA-1 hash
+ Refer to commits in other repositories by public web address (such as
https://fuchsia.googlesource.com/fuchsia/+/67fec6d)
1. Other entities which may not make sense to arbitrary future readers
Adding a `Test:` line to the commit message is encouraged. A `Test:` line
should:
1. Justify that any behavior changes or additions are thoroughly tested.
1. Describe how to run new/affected test cases.
For example: ``Test: Added new unit tests. `fx test netstack-gotests` ``.
## Code Review Guidelines
### Code Review Flow
The following code review guidelines are adopted within the Netstack team:
**Authors:**
- When your CL is ready for review, request a review from a team member listed
in the closest OWNERS file.
- If your CL introduces non-trivial changes, also add a secondary reviewer
picked from `src/connectivity/network/OWNERS`. This should happen
simultaneously to requesting review from owners. You can choose any team
member you want; Consider the following criteria:
- Listed as readability reviewer in
[`src/connectivity/network/tests/integration/common/OWNERS`][netemul-owners]
if the CL consists primarily of changes to netemul integration tests.
- Ramping up in the target area.
- Working in tangentially related areas.
- Has language/patterns experience.
- Before adding reviewer from the closest OWNERS file, you can add the Google
group fuchsia-netstack-reviews@google.com as a reviewer. The gwsq bot will
pick a random reviewer from the group.
- Acquiring +2 from both reviewers is strongly recommended, but not strictly
necessary.
**Reviewers:**
- If you feel you don’t have enough local knowledge to +2, the right thing to do
is perform a best effort review in terms of language use, style, patterns, or
generalities and +1.
- Always review the code as if you were the sole reviewer.
- Avoid delegating parts of the review if possible - “you’re more familiar with
this part”.
- Engage in code reviews meaningfully, regardless of local ownership.
- Owner reviewers are encouraged to allow secondary reviewers to take first
pass. Once the CL is +2d by the owner, there’s a strong anchoring effect that
reduces the challenge and learning opportunity. Secondary reviewers may
request taking first pass or owner reviewers may grant secondaries first pass
by posting a comment on the CL stating that intent. The inverse, i.e.
secondary reviewers requesting that owners take first pass, is discouraged.
Note that this scheme can increase latency for reviews, which is a negative side
effect we'd like to minimize. Try to decrease your latency when either being
asked for review or addressing comments. We strive to keep latency **under 24h**
for both authors and reviewers. Don't be afraid to ping if it's been over 24h.
Gerrit notification settings and smart e-mail filters can be a big help to drive
those interrupts. Also, don't be afraid to ping for reviews.
Area owners are encouraged to create Gerrit notification filters for their areas
of interest to help enforce these guidelines and design vision.
## Tips & Tricks
### `fx set`
Run the following command to build all tests and their dependencies:
```
fx set core.x64 --with //src/connectivity/network:tests
```
If you're working on changes that affect `fdio` and `third_party/go`, add:
```
--with //sdk/lib/fdio:tests --with //third_party/go:tests
```
[getting_started]: /docs/get-started
[contribute_changes]: /docs/development/source_code/contribute_changes.md
[spawn]: https://fuchsia.googlesource.com/fuchsia/+/a874276/src/lib/fuchsia-async/src/executor.rs#30
[select]: https://docs.rs/futures/0.3.4/futures/macro.select.html
[spawn_select]: https://fuchsia.googlesource.com/fuchsia/+/0c00fd3%5E%21/#F3
[spawn_await]: https://fuchsia.googlesource.com/fuchsia/+/038d2b9%5E%21/#F0
[magic_number]: https://en.wikipedia.org/wiki/Magic_number_(programming)
[rfc_process]: /docs/contribute/governance/rfcs/0001_rfc_process.md
[commit_guidelines]: https://www.git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project#_commit_guidelines
[flutter_philosophy]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#philosophy
[testability_rubrics]: /docs/development/testing/testability_rubric.md
[tests_as_components]: /docs/development/testing/run_fuchsia_tests.md
[netemul]: /src/connectivity/network/testing/netemul/README.md
[change_detector_tests]: https://testing.googleblog.com/2015/01/testing-on-toilet-change-detector-tests.html
[rust_65218]: https://github.com/rust-lang/rust/issues/65218
[go_test_flags]: https://golang.org/cmd/go/#hdr-Testing_flags
[gtest_test_flags]: https://github.com/google/googletest/blob/main/docs/advanced.md#repeating-the-tests
[`fuchsia_async::Executor::new_with_fake_time`]: https://fuchsia.googlesource.com/fuchsia/+/a874276/src/lib/fuchsia-async/src/executor.rs#345
[fake-clock]: https://fuchsia.googlesource.com/fuchsia/+/a874276/src/lib/fake-clock
[determinism]: /docs/contribute/testing/best-practices.md#write_reproducible_deterministic_tests
[netemul-owners]: https://fuchsia.googlesource.com/fuchsia/+/refs/heads/main/src/connectivity/network/tests/integration/common/OWNERS