Document end to end testing. (#2275)

diff --git a/pkgs/watcher/test/directory_watcher/README.md b/pkgs/watcher/test/directory_watcher/README.md
new file mode 100644
index 0000000..8e1744c
--- /dev/null
+++ b/pkgs/watcher/test/directory_watcher/README.md
@@ -0,0 +1,96 @@
+# End to end testing
+
+The `DirectoryWatcher` implementations combine information from OS events and
+filesystem polling, which leads to plenty of opportunities for data races
+between the two. There are also various data races related to OS event
+ordering and batching.
+
+The tests in `end_to_end_tests.dart` protect against both logical errors and
+races.
+
+All the tests work the same way: `ClientSimulator` uses a directory watcher to
+track the state of a directory, a series of filesystem changes are made in that
+directory using `FileChanger`, then `ClientSimulator` compares its inferred
+state with the actual state on disk.
+
+File contents vary only by length, so the file contents can be given as a single
+number in the logs.
+
+There are three types of error possible:
+
+ - `ClientSimulator` thinks a file exists on disk, but it doesn't; "missing delete event"
+ - `ClientSimulator` does not know about a file that exists on disk; "missing add event"
+ - `ClientSimulator` knows about a file that exists on disk, but has not read
+   it after it was updated, meaning it has a wrong value for its
+   contents/length; "missing modify event"
+
+## Example data race
+
+An example sequence of file operations that can cause a data race is moving a
+directory then making further modifications inside it. The OS events report the
+"new" directory but not its contents, so `DirectoryWatcher` has to list the
+contents. The list results and the OS events from subsequent operations can
+give contradictory information about the same file, with no way to know which
+is more recent and so correct. The implementations created with the help of
+these tests aim to detect such ambiguity and resolve it by polling again after
+the event arrives.
+
+## Standalone tests
+
+The end to end tests that run on CI include a series of seeded pseudorandom file
+operation batches and a set of hardcoded tests that were derived from
+interesting random runs. These guard against common data races.
+
+But, they don't run for long enough on CI to give high confidence that there are
+no data races.
+
+So, when making changes that might affect data races it is recommended to run
+a longer "standalone" end to end test run. This should be done on whichever
+platform(s) are affected, Windows, Mac and/or Linux, by running the end to end
+test multiple times in parallel and overnight.
+
+```
+# Launch in multiple terminals, enough to use 100% CPU.
+dart test/directory_watcher/end_to_end_test_runner.dart random
+
+# Or on Linux, install `parallel` and use that to run any number in parallel.
+parallel --ungroup --halt now,done=1 \
+    -j 100 ::: \
+    $(for i in (seq 1 100); do echo 'dart test/directory_watcher/end_to_end_test_runner.dart'; end)
+```
+
+Run in this way the test runs until it hits a failure. If it does, it prints
+a link to a log which shows a combination of file operations `F`, watcher
+internals `W` and the events seen by the `ClientSimulator` marked `C`.
+It also prints the seed of the failure, which can be used to run the same
+pseudorandom batch of file operations to see if the exact same failure can
+be reproduced:
+
+```
+dart test/directory_watcher/end_to_end_test_runner.dart seed 42
+```
+
+Another way to rerun the exact same sequence of operations that failed is to
+copy the failure log into `end_to_end_tests.dart` as a test case. Only the lines
+that are file operations marked with `F` are needed. Then run:
+
+```
+dart test/directory_watcher/end_to_end_test_runner.dart replay <test name>
+```
+
+If a failure can be reproduced in this way then you can try removing
+irrelevant-seeming parts of the log until you have a minimal repro case. Note
+that a file operation that can't be carried out, for example a move into a
+directory that does not exist, is silently skipped over and does nothing.
+
+## False negatives
+
+The standalone end to end tests have one known "false negative" issue, which
+is that very occasionally and under heavy load the test might not wait long
+enough before deciding that `ClientWatcher` has incorrect state. This can be
+noticed in the failure log if all the wrong tracking is about file events at the
+end of the run, and with no watcher log entries afterwards. Such failures can
+be ignored.
+
+TODO(davidmorgan): detect this automatically and wait longer instead of failing
+the test.
\ No newline at end of file
diff --git a/pkgs/watcher/test/directory_watcher/end_to_end_tests.dart b/pkgs/watcher/test/directory_watcher/end_to_end_tests.dart
index 5e0b212..4399f7d 100644
--- a/pkgs/watcher/test/directory_watcher/end_to_end_tests.dart
+++ b/pkgs/watcher/test/directory_watcher/end_to_end_tests.dart
@@ -19,6 +19,8 @@
 ///
 /// `end_to_end_test_runner` can be run as a binary to try random file changes
 /// until a failure, it outputs a log which can be turned into a test case here.
+///
+/// See `README.md` for more detail.
 void endToEndTests() {
   // Random test to cover a wide range of cases.
   test('end to end test: random', timeout: const Timeout(Duration(minutes: 10)),