|
| 1 | +# End to end testing |
| 2 | + |
| 3 | +The `DirectoryWatcher` implementations combine information from OS events and |
| 4 | +filesystem polling, which leads to plenty of opportunities for data races |
| 5 | +between the two. There are also various data races related to OS event |
| 6 | +ordering and batching. |
| 7 | + |
| 8 | +The tests in `end_to_end_tests.dart` protect against both logical errors and |
| 9 | +races. |
| 10 | + |
| 11 | +All the tests work the same way: `ClientSimulator` uses a directory watcher to |
| 12 | +track the state of a directory, a series of filesystem changes are made in that |
| 13 | +directory using `FileChanger`, then `ClientSimulator` compares its inferred |
| 14 | +state with the actual state on disk. |
| 15 | + |
| 16 | +File contents vary only by length, so the file contents can be given as a single |
| 17 | +number in the logs. |
| 18 | + |
| 19 | +There are three types of error possible: |
| 20 | + |
| 21 | + - `ClientSimulator` thinks a file exists on disk, but it doesn't; "missing delete event" |
| 22 | + - `ClientSimulator` does not know about a file that exists on disk; "missing add event" |
| 23 | + - `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" |
| 24 | + |
| 25 | +## Example data race |
| 26 | + |
| 27 | +An example sequence of file operations that can cause a data race is moving a |
| 28 | +directory then making further modifications inside it. The OS events report the |
| 29 | +"new" directory but not its contents, so `DirectoryWatcher` has to list the |
| 30 | +contents. The list results and the OS events from subsequent operations can |
| 31 | +give contradictory information about the same file, with no way to know which |
| 32 | +is more recent and so correct. The implementations created with the help of |
| 33 | +these tests aim to detect such ambiguity and resolve it by polling again after |
| 34 | +the event arrives. |
| 35 | + |
| 36 | +## Standalone tests |
| 37 | + |
| 38 | +The end to end tests that run on CI include a series of seeded pseudorandom file |
| 39 | +operation batches and a set of hardcoded tests that were derived from |
| 40 | +interesting random runs. These guard against common data races. |
| 41 | + |
| 42 | +But, they don't run for long enough on CI to give high confidence that there are |
| 43 | +no data races. |
| 44 | + |
| 45 | +So, when making changes that might affect data races it is recommended to run |
| 46 | +a longer "standalone" end to end test run. This should be done on whichever |
| 47 | +platform(s) are affected, Windows, Mac and/or Linux, by running the end to end |
| 48 | +test multiple times in parallel and overnight. |
| 49 | + |
| 50 | +``` |
| 51 | +# Launch in multiple terminals, enough to use 100% CPU. |
| 52 | +dart test/directory_watcher/end_to_end_test_runner.dart random |
| 53 | +
|
| 54 | +# Or on Linux, install `parallel` and use that to run any number in parallel. |
| 55 | +parallel --ungroup --halt now,done=1 \ |
| 56 | + -j 100 ::: \ |
| 57 | + $(for i in (seq 1 100); do echo 'dart test/directory_watcher/end_to_end_test_runner.dart'; end) |
| 58 | +``` |
| 59 | + |
| 60 | +Run in this way the test runs until it hits a failure. If it does, it prints |
| 61 | +a link to a log which shows a combination of file operations `F`, watcher |
| 62 | +internals `W` and the events seen by the `ClientSimulator` marked `C`. |
| 63 | +It also prints the seed of the failure, which can be used to run the same |
| 64 | +pseudorandom batch of file operations to see if the exact same failure can |
| 65 | +be reproduced: |
| 66 | + |
| 67 | +``` |
| 68 | +dart test/directory_watcher/end_to_end_test_runner.dart seed 42 |
| 69 | +``` |
| 70 | + |
| 71 | +Another way to rerun the exact same sequence of operations that failed is to |
| 72 | +copy the failure log into `end_to_end_tests.dart` as a test case. Only the lines |
| 73 | +that are file operations marked with `F` are needed. Then run: |
| 74 | + |
| 75 | +``` |
| 76 | +dart test/directory_watcher/end_to_end_test_runner.dart replay <test name> |
| 77 | +``` |
| 78 | + |
| 79 | +If a failure can be reproduced in this way then you can try removing |
| 80 | +irrelevant-seeming parts of the log until you have a minimal repro case. Note |
| 81 | +that a file operation that can't be carried out, for example a move into a |
| 82 | +directory that does not exist, is silently skipped over and does nothing. |
| 83 | + |
| 84 | +### False negatives |
| 85 | + |
| 86 | +The standalone end to end tests have one known "false negative" issue, which |
| 87 | +is that very occasionally and under heavy load the test might not wait long |
| 88 | +enough before deciding that `ClientWatcher` has incorrect state. This can be |
| 89 | +noticed in the failure log if all the wrong tracking is about file events at the |
| 90 | +end of the run, and with no watcher log entries afterwards. Such failures can |
| 91 | +be ignored. |
| 92 | + |
| 93 | +TODO(davidmorgan): detect this automatically and wait longer instead of failing |
| 94 | +the test. |
0 commit comments