tree: 72c57d0d7399501c0880c1fdb53149669c2f6c93 [path history] [tgz]
  1. polling/
  2. recursive/
  3. client_simulator.dart
  4. end_to_end_test_runner.dart
  5. end_to_end_tests.dart
  6. file_changer.dart
  7. file_tests.dart
  8. link_tests.dart
  9. linux_test.dart
  10. macos_test.dart
  11. polling_test.dart
  12. README.md
  13. relative_directory_test.dart
  14. windows_isolate_test.dart
  15. windows_test.dart
pkgs/watcher/test/directory_watcher/README.md

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.