| # 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. |