t7527: test status with untracked-cache and fsmonitor--daemon
[git.git] / t / t7527-builtin-fsmonitor.sh
blobbd0c952a11659cb10216aa318e83c968a0cc80cb
1 #!/bin/sh
3 test_description='built-in file system watcher'
5 . ./test-lib.sh
7 if ! test_have_prereq FSMONITOR_DAEMON
8 then
9 skip_all="fsmonitor--daemon is not supported on this platform"
10 test_done
13 stop_daemon_delete_repo () {
14 r=$1 &&
15 test_might_fail git -C $r fsmonitor--daemon stop &&
16 rm -rf $1
19 start_daemon () {
20 r= tf= t2= tk= &&
22 while test "$#" -ne 0
24 case "$1" in
25 -C)
26 r="-C ${2?}"
27 shift
29 --tf)
30 tf="${2?}"
31 shift
33 --t2)
34 t2="${2?}"
35 shift
37 --tk)
38 tk="${2?}"
39 shift
41 -*)
42 BUG "error: unknown option: '$1'"
45 BUG "error: unbound argument: '$1'"
47 esac
48 shift
49 done &&
52 if test -n "$tf"
53 then
54 GIT_TRACE_FSMONITOR="$tf"
55 export GIT_TRACE_FSMONITOR
56 fi &&
58 if test -n "$t2"
59 then
60 GIT_TRACE2_PERF="$t2"
61 export GIT_TRACE2_PERF
62 fi &&
64 if test -n "$tk"
65 then
66 GIT_TEST_FSMONITOR_TOKEN="$tk"
67 export GIT_TEST_FSMONITOR_TOKEN
68 fi &&
70 git $r fsmonitor--daemon start &&
71 git $r fsmonitor--daemon status
75 # Is a Trace2 data event present with the given catetory and key?
76 # We do not care what the value is.
78 have_t2_data_event () {
79 c=$1 &&
80 k=$2 &&
82 grep -e '"event":"data".*"category":"'"$c"'".*"key":"'"$k"'"'
85 test_expect_success 'explicit daemon start and stop' '
86 test_when_finished "stop_daemon_delete_repo test_explicit" &&
88 git init test_explicit &&
89 start_daemon -C test_explicit &&
91 git -C test_explicit fsmonitor--daemon stop &&
92 test_must_fail git -C test_explicit fsmonitor--daemon status
95 test_expect_success 'implicit daemon start' '
96 test_when_finished "stop_daemon_delete_repo test_implicit" &&
98 git init test_implicit &&
99 test_must_fail git -C test_implicit fsmonitor--daemon status &&
101 # query will implicitly start the daemon.
103 # for test-script simplicity, we send a V1 timestamp rather than
104 # a V2 token. either way, the daemon response to any query contains
105 # a new V2 token. (the daemon may complain that we sent a V1 request,
106 # but this test case is only concerned with whether the daemon was
107 # implicitly started.)
109 GIT_TRACE2_EVENT="$PWD/.git/trace" \
110 test-tool -C test_implicit fsmonitor-client query --token 0 >actual &&
111 nul_to_q <actual >actual.filtered &&
112 grep "builtin:" actual.filtered &&
114 # confirm that a daemon was started in the background.
116 # since the mechanism for starting the background daemon is platform
117 # dependent, just confirm that the foreground command received a
118 # response from the daemon.
120 have_t2_data_event fsm_client query/response-length <.git/trace &&
122 git -C test_implicit fsmonitor--daemon status &&
123 git -C test_implicit fsmonitor--daemon stop &&
124 test_must_fail git -C test_implicit fsmonitor--daemon status
127 test_expect_success 'implicit daemon stop (delete .git)' '
128 test_when_finished "stop_daemon_delete_repo test_implicit_1" &&
130 git init test_implicit_1 &&
132 start_daemon -C test_implicit_1 &&
134 # deleting the .git directory will implicitly stop the daemon.
135 rm -rf test_implicit_1/.git &&
137 # [1] Create an empty .git directory so that the following Git
138 # command will stay relative to the `-C` directory.
140 # Without this, the Git command will override the requested
141 # -C argument and crawl out to the containing Git source tree.
142 # This would make the test result dependent upon whether we
143 # were using fsmonitor on our development worktree.
145 sleep 1 &&
146 mkdir test_implicit_1/.git &&
148 test_must_fail git -C test_implicit_1 fsmonitor--daemon status
151 test_expect_success 'implicit daemon stop (rename .git)' '
152 test_when_finished "stop_daemon_delete_repo test_implicit_2" &&
154 git init test_implicit_2 &&
156 start_daemon -C test_implicit_2 &&
158 # renaming the .git directory will implicitly stop the daemon.
159 mv test_implicit_2/.git test_implicit_2/.xxx &&
161 # See [1] above.
163 sleep 1 &&
164 mkdir test_implicit_2/.git &&
166 test_must_fail git -C test_implicit_2 fsmonitor--daemon status
169 test_expect_success 'cannot start multiple daemons' '
170 test_when_finished "stop_daemon_delete_repo test_multiple" &&
172 git init test_multiple &&
174 start_daemon -C test_multiple &&
176 test_must_fail git -C test_multiple fsmonitor--daemon start 2>actual &&
177 grep "fsmonitor--daemon is already running" actual &&
179 git -C test_multiple fsmonitor--daemon stop &&
180 test_must_fail git -C test_multiple fsmonitor--daemon status
183 # These tests use the main repo in the trash directory
185 test_expect_success 'setup' '
186 >tracked &&
187 >modified &&
188 >delete &&
189 >rename &&
190 mkdir dir1 &&
191 >dir1/tracked &&
192 >dir1/modified &&
193 >dir1/delete &&
194 >dir1/rename &&
195 mkdir dir2 &&
196 >dir2/tracked &&
197 >dir2/modified &&
198 >dir2/delete &&
199 >dir2/rename &&
200 mkdir dirtorename &&
201 >dirtorename/a &&
202 >dirtorename/b &&
204 cat >.gitignore <<-\EOF &&
205 .gitignore
206 expect*
207 actual*
208 flush*
209 trace*
212 git -c core.fsmonitor=false add . &&
213 test_tick &&
214 git -c core.fsmonitor=false commit -m initial &&
216 git config core.fsmonitor true
219 # The test already explicitly stopped (or tried to stop) the daemon.
220 # This is here in case something else fails first.
222 redundant_stop_daemon () {
223 test_might_fail git fsmonitor--daemon stop
226 test_expect_success 'update-index implicitly starts daemon' '
227 test_when_finished redundant_stop_daemon &&
229 test_must_fail git fsmonitor--daemon status &&
231 GIT_TRACE2_EVENT="$PWD/.git/trace_implicit_1" \
232 git update-index --fsmonitor &&
234 git fsmonitor--daemon status &&
235 test_might_fail git fsmonitor--daemon stop &&
237 # Confirm that the trace2 log contains a record of the
238 # daemon starting.
239 test_subcommand git fsmonitor--daemon start <.git/trace_implicit_1
242 test_expect_success 'status implicitly starts daemon' '
243 test_when_finished redundant_stop_daemon &&
245 test_must_fail git fsmonitor--daemon status &&
247 GIT_TRACE2_EVENT="$PWD/.git/trace_implicit_2" \
248 git status >actual &&
250 git fsmonitor--daemon status &&
251 test_might_fail git fsmonitor--daemon stop &&
253 # Confirm that the trace2 log contains a record of the
254 # daemon starting.
255 test_subcommand git fsmonitor--daemon start <.git/trace_implicit_2
258 edit_files () {
259 echo 1 >modified &&
260 echo 2 >dir1/modified &&
261 echo 3 >dir2/modified &&
262 >dir1/untracked
265 delete_files () {
266 rm -f delete &&
267 rm -f dir1/delete &&
268 rm -f dir2/delete
271 create_files () {
272 echo 1 >new &&
273 echo 2 >dir1/new &&
274 echo 3 >dir2/new
277 rename_files () {
278 mv rename renamed &&
279 mv dir1/rename dir1/renamed &&
280 mv dir2/rename dir2/renamed
283 file_to_directory () {
284 rm -f delete &&
285 mkdir delete &&
286 echo 1 >delete/new
289 directory_to_file () {
290 rm -rf dir1 &&
291 echo 1 >dir1
294 # The next few test cases confirm that our fsmonitor daemon sees each type
295 # of OS filesystem notification that we care about. At this layer we just
296 # ensure we are getting the OS notifications and do not try to confirm what
297 # is reported by `git status`.
299 # We run a simple query after modifying the filesystem just to introduce
300 # a bit of a delay so that the trace logging from the daemon has time to
301 # get flushed to disk.
303 # We `reset` and `clean` at the bottom of each test (and before stopping the
304 # daemon) because these commands might implicitly restart the daemon.
306 clean_up_repo_and_stop_daemon () {
307 git reset --hard HEAD &&
308 git clean -fd &&
309 test_might_fail git fsmonitor--daemon stop &&
310 rm -f .git/trace
313 test_expect_success 'edit some files' '
314 test_when_finished clean_up_repo_and_stop_daemon &&
316 start_daemon --tf "$PWD/.git/trace" &&
318 edit_files &&
320 test-tool fsmonitor-client query --token 0 &&
322 grep "^event: dir1/modified$" .git/trace &&
323 grep "^event: dir2/modified$" .git/trace &&
324 grep "^event: modified$" .git/trace &&
325 grep "^event: dir1/untracked$" .git/trace
328 test_expect_success 'create some files' '
329 test_when_finished clean_up_repo_and_stop_daemon &&
331 start_daemon --tf "$PWD/.git/trace" &&
333 create_files &&
335 test-tool fsmonitor-client query --token 0 &&
337 grep "^event: dir1/new$" .git/trace &&
338 grep "^event: dir2/new$" .git/trace &&
339 grep "^event: new$" .git/trace
342 test_expect_success 'delete some files' '
343 test_when_finished clean_up_repo_and_stop_daemon &&
345 start_daemon --tf "$PWD/.git/trace" &&
347 delete_files &&
349 test-tool fsmonitor-client query --token 0 &&
351 grep "^event: dir1/delete$" .git/trace &&
352 grep "^event: dir2/delete$" .git/trace &&
353 grep "^event: delete$" .git/trace
356 test_expect_success 'rename some files' '
357 test_when_finished clean_up_repo_and_stop_daemon &&
359 start_daemon --tf "$PWD/.git/trace" &&
361 rename_files &&
363 test-tool fsmonitor-client query --token 0 &&
365 grep "^event: dir1/rename$" .git/trace &&
366 grep "^event: dir2/rename$" .git/trace &&
367 grep "^event: rename$" .git/trace &&
368 grep "^event: dir1/renamed$" .git/trace &&
369 grep "^event: dir2/renamed$" .git/trace &&
370 grep "^event: renamed$" .git/trace
373 test_expect_success 'rename directory' '
374 test_when_finished clean_up_repo_and_stop_daemon &&
376 start_daemon --tf "$PWD/.git/trace" &&
378 mv dirtorename dirrenamed &&
380 test-tool fsmonitor-client query --token 0 &&
382 grep "^event: dirtorename/*$" .git/trace &&
383 grep "^event: dirrenamed/*$" .git/trace
386 test_expect_success 'file changes to directory' '
387 test_when_finished clean_up_repo_and_stop_daemon &&
389 start_daemon --tf "$PWD/.git/trace" &&
391 file_to_directory &&
393 test-tool fsmonitor-client query --token 0 &&
395 grep "^event: delete$" .git/trace &&
396 grep "^event: delete/new$" .git/trace
399 test_expect_success 'directory changes to a file' '
400 test_when_finished clean_up_repo_and_stop_daemon &&
402 start_daemon --tf "$PWD/.git/trace" &&
404 directory_to_file &&
406 test-tool fsmonitor-client query --token 0 &&
408 grep "^event: dir1$" .git/trace
411 # The next few test cases exercise the token-resync code. When filesystem
412 # drops events (because of filesystem velocity or because the daemon isn't
413 # polling fast enough), we need to discard the cached data (relative to the
414 # current token) and start collecting events under a new token.
416 # the 'test-tool fsmonitor-client flush' command can be used to send a
417 # "flush" message to a running daemon and ask it to do a flush/resync.
419 test_expect_success 'flush cached data' '
420 test_when_finished "stop_daemon_delete_repo test_flush" &&
422 git init test_flush &&
424 start_daemon -C test_flush --tf "$PWD/.git/trace_daemon" --tk true &&
426 # The daemon should have an initial token with no events in _0 and
427 # then a few (probably platform-specific number of) events in _1.
428 # These should both have the same <token_id>.
430 test-tool -C test_flush fsmonitor-client query --token "builtin:test_00000001:0" >actual_0 &&
431 nul_to_q <actual_0 >actual_q0 &&
433 >test_flush/file_1 &&
434 >test_flush/file_2 &&
436 test-tool -C test_flush fsmonitor-client query --token "builtin:test_00000001:0" >actual_1 &&
437 nul_to_q <actual_1 >actual_q1 &&
439 grep "file_1" actual_q1 &&
441 # Force a flush. This will change the <token_id>, reset the <seq_nr>, and
442 # flush the file data. Then create some events and ensure that the file
443 # again appears in the cache. It should have the new <token_id>.
445 test-tool -C test_flush fsmonitor-client flush >flush_0 &&
446 nul_to_q <flush_0 >flush_q0 &&
447 grep "^builtin:test_00000002:0Q/Q$" flush_q0 &&
449 test-tool -C test_flush fsmonitor-client query --token "builtin:test_00000002:0" >actual_2 &&
450 nul_to_q <actual_2 >actual_q2 &&
452 grep "^builtin:test_00000002:0Q$" actual_q2 &&
454 >test_flush/file_3 &&
456 test-tool -C test_flush fsmonitor-client query --token "builtin:test_00000002:0" >actual_3 &&
457 nul_to_q <actual_3 >actual_q3 &&
459 grep "file_3" actual_q3
462 # The next few test cases create repos where the .git directory is NOT
463 # inside the one of the working directory. That is, where .git is a file
464 # that points to a directory elsewhere. This happens for submodules and
465 # non-primary worktrees.
467 test_expect_success 'setup worktree base' '
468 git init wt-base &&
469 echo 1 >wt-base/file1 &&
470 git -C wt-base add file1 &&
471 git -C wt-base commit -m "c1"
474 test_expect_success 'worktree with .git file' '
475 git -C wt-base worktree add ../wt-secondary &&
477 start_daemon -C wt-secondary \
478 --tf "$PWD/trace_wt_secondary" \
479 --t2 "$PWD/trace2_wt_secondary" &&
481 git -C wt-secondary fsmonitor--daemon stop &&
482 test_must_fail git -C wt-secondary fsmonitor--daemon status
485 # NEEDSWORK: Repeat one of the "edit" tests on wt-secondary and
486 # confirm that we get the same events and behavior -- that is, that
487 # fsmonitor--daemon correctly watches BOTH the working directory and
488 # the external GITDIR directory and behaves the same as when ".git"
489 # is a directory inside the working directory.
491 test_expect_success 'cleanup worktrees' '
492 stop_daemon_delete_repo wt-secondary &&
493 stop_daemon_delete_repo wt-base
496 # The next few tests perform arbitrary/contrived file operations and
497 # confirm that status is correct. That is, that the data (or lack of
498 # data) from fsmonitor doesn't cause incorrect results. And doesn't
499 # cause incorrect results when the untracked-cache is enabled.
501 test_lazy_prereq UNTRACKED_CACHE '
502 git update-index --test-untracked-cache
505 test_expect_success 'Matrix: setup for untracked-cache,fsmonitor matrix' '
506 test_unconfig core.fsmonitor &&
507 git update-index --no-fsmonitor &&
508 test_might_fail git fsmonitor--daemon stop
511 matrix_clean_up_repo () {
512 git reset --hard HEAD &&
513 git clean -fd
516 matrix_try () {
517 uc=$1 &&
518 fsm=$2 &&
519 fn=$3 &&
521 if test $uc = true && test $fsm = false
522 then
523 # The untracked-cache is buggy when FSMonitor is
524 # DISABLED, so skip the tests for this matrix
525 # combination.
527 # We've observed random, occasional test failures on
528 # Windows and MacOS when the UC is turned on and FSM
529 # is turned off. These are rare, but they do happen
530 # indicating that it is probably a race condition within
531 # the untracked cache itself.
533 # It usually happens when a test does F/D trickery and
534 # then the NEXT test fails because of extra status
535 # output from stale UC data from the previous test.
537 # Since FSMonitor is not involved in the error, skip
538 # the tests for this matrix combination.
540 return 0
541 fi &&
543 test_expect_success "Matrix[uc:$uc][fsm:$fsm] $fn" '
544 matrix_clean_up_repo &&
545 $fn &&
546 if test $uc = false && test $fsm = false
547 then
548 git status --porcelain=v1 >.git/expect.$fn
549 else
550 git status --porcelain=v1 >.git/actual.$fn &&
551 test_cmp .git/expect.$fn .git/actual.$fn
556 uc_values="false"
557 test_have_prereq UNTRACKED_CACHE && uc_values="false true"
558 for uc_val in $uc_values
560 if test $uc_val = false
561 then
562 test_expect_success "Matrix[uc:$uc_val] disable untracked cache" '
563 git config core.untrackedcache false &&
564 git update-index --no-untracked-cache
566 else
567 test_expect_success "Matrix[uc:$uc_val] enable untracked cache" '
568 git config core.untrackedcache true &&
569 git update-index --untracked-cache
573 fsm_values="false true"
574 for fsm_val in $fsm_values
576 if test $fsm_val = false
577 then
578 test_expect_success "Matrix[uc:$uc_val][fsm:$fsm_val] disable fsmonitor" '
579 test_unconfig core.fsmonitor &&
580 git update-index --no-fsmonitor &&
581 test_might_fail git fsmonitor--daemon stop
583 else
584 test_expect_success "Matrix[uc:$uc_val][fsm:$fsm_val] enable fsmonitor" '
585 git config core.fsmonitor true &&
586 git fsmonitor--daemon start &&
587 git update-index --fsmonitor
591 matrix_try $uc_val $fsm_val edit_files
592 matrix_try $uc_val $fsm_val delete_files
593 matrix_try $uc_val $fsm_val create_files
594 matrix_try $uc_val $fsm_val rename_files
595 matrix_try $uc_val $fsm_val file_to_directory
596 matrix_try $uc_val $fsm_val directory_to_file
598 if test $fsm_val = true
599 then
600 test_expect_success "Matrix[uc:$uc_val][fsm:$fsm_val] disable fsmonitor at end" '
601 test_unconfig core.fsmonitor &&
602 git update-index --no-fsmonitor &&
603 test_might_fail git fsmonitor--daemon stop
606 done
607 done
609 test_done