summaryrefslogtreecommitdiff
path: root/t/t7527-builtin-fsmonitor.sh
diff options
context:
space:
mode:
Diffstat (limited to 't/t7527-builtin-fsmonitor.sh')
-rwxr-xr-xt/t7527-builtin-fsmonitor.sh401
1 files changed, 397 insertions, 4 deletions
diff --git a/t/t7527-builtin-fsmonitor.sh b/t/t7527-builtin-fsmonitor.sh
index bd0c952..56c0dff 100755
--- a/t/t7527-builtin-fsmonitor.sh
+++ b/t/t7527-builtin-fsmonitor.sh
@@ -124,6 +124,36 @@ test_expect_success 'implicit daemon start' '
test_must_fail git -C test_implicit fsmonitor--daemon status
'
+# Verify that the daemon has shutdown. Spin a few seconds to
+# make the test a little more robust during CI testing.
+#
+# We're looking for an implicit shutdown, such as when we delete or
+# rename the ".git" directory. Our delete/rename will cause a file
+# system event that the daemon will see and the daemon will
+# auto-shutdown as soon as it sees it. But this is racy with our `git
+# fsmonitor--daemon status` commands (and we cannot use a cookie file
+# here to help us). So spin a little and give the daemon a chance to
+# see the event. (This is primarily for underpowered CI build/test
+# machines (where it might take a moment to wake and reschedule the
+# daemon process) to avoid false alarms during test runs.)
+#
+IMPLICIT_TIMEOUT=5
+
+verify_implicit_shutdown () {
+ r=$1 &&
+
+ k=0 &&
+ while test "$k" -lt $IMPLICIT_TIMEOUT
+ do
+ git -C $r fsmonitor--daemon status || return 0
+
+ sleep 1
+ k=$(( $k + 1 ))
+ done &&
+
+ return 1
+}
+
test_expect_success 'implicit daemon stop (delete .git)' '
test_when_finished "stop_daemon_delete_repo test_implicit_1" &&
@@ -142,10 +172,9 @@ test_expect_success 'implicit daemon stop (delete .git)' '
# This would make the test result dependent upon whether we
# were using fsmonitor on our development worktree.
#
- sleep 1 &&
mkdir test_implicit_1/.git &&
- test_must_fail git -C test_implicit_1 fsmonitor--daemon status
+ verify_implicit_shutdown test_implicit_1
'
test_expect_success 'implicit daemon stop (rename .git)' '
@@ -160,10 +189,70 @@ test_expect_success 'implicit daemon stop (rename .git)' '
# See [1] above.
#
- sleep 1 &&
mkdir test_implicit_2/.git &&
- test_must_fail git -C test_implicit_2 fsmonitor--daemon status
+ verify_implicit_shutdown test_implicit_2
+'
+
+# File systems on Windows may or may not have shortnames.
+# This is a volume-specific setting on modern systems.
+# "C:/" drives are required to have them enabled. Other
+# hard drives default to disabled.
+#
+# This is a crude test to see if shortnames are enabled
+# on the volume containing the test directory. It is
+# crude, but it does not require elevation like `fsutil`.
+#
+test_lazy_prereq SHORTNAMES '
+ mkdir .foo &&
+ test -d "FOO~1"
+'
+
+# Here we assume that the shortname of ".git" is "GIT~1".
+test_expect_success MINGW,SHORTNAMES 'implicit daemon stop (rename GIT~1)' '
+ test_when_finished "stop_daemon_delete_repo test_implicit_1s" &&
+
+ git init test_implicit_1s &&
+
+ start_daemon -C test_implicit_1s &&
+
+ # renaming the .git directory will implicitly stop the daemon.
+ # this moves {.git, GIT~1} to {.gitxyz, GITXYZ~1}.
+ # the rename-from FS Event will contain the shortname.
+ #
+ mv test_implicit_1s/GIT~1 test_implicit_1s/.gitxyz &&
+
+ # See [1] above.
+ # this moves {.gitxyz, GITXYZ~1} to {.git, GIT~1}.
+ mv test_implicit_1s/.gitxyz test_implicit_1s/.git &&
+
+ verify_implicit_shutdown test_implicit_1s
+'
+
+# Here we first create a file with LONGNAME of "GIT~1" before
+# we create the repo. This will cause the shortname of ".git"
+# to be "GIT~2".
+test_expect_success MINGW,SHORTNAMES 'implicit daemon stop (rename GIT~2)' '
+ test_when_finished "stop_daemon_delete_repo test_implicit_1s2" &&
+
+ mkdir test_implicit_1s2 &&
+ echo HELLO >test_implicit_1s2/GIT~1 &&
+ git init test_implicit_1s2 &&
+
+ test_path_is_file test_implicit_1s2/GIT~1 &&
+ test_path_is_dir test_implicit_1s2/GIT~2 &&
+
+ start_daemon -C test_implicit_1s2 &&
+
+ # renaming the .git directory will implicitly stop the daemon.
+ # the rename-from FS Event will contain the shortname.
+ #
+ mv test_implicit_1s2/GIT~2 test_implicit_1s2/.gitxyz &&
+
+ # See [1] above.
+ mv test_implicit_1s2/.gitxyz test_implicit_1s2/.git &&
+
+ verify_implicit_shutdown test_implicit_1s2
'
test_expect_success 'cannot start multiple daemons' '
@@ -209,6 +298,16 @@ test_expect_success 'setup' '
trace*
EOF
+ mkdir -p T1/T2/T3/T4 &&
+ echo 1 >T1/F1 &&
+ echo 1 >T1/T2/F1 &&
+ echo 1 >T1/T2/T3/F1 &&
+ echo 1 >T1/T2/T3/T4/F1 &&
+ echo 2 >T1/F2 &&
+ echo 2 >T1/T2/F2 &&
+ echo 2 >T1/T2/T3/F2 &&
+ echo 2 >T1/T2/T3/T4/F2 &&
+
git -c core.fsmonitor=false add . &&
test_tick &&
git -c core.fsmonitor=false commit -m initial &&
@@ -291,6 +390,19 @@ directory_to_file () {
echo 1 >dir1
}
+move_directory_contents_deeper() {
+ mkdir T1/_new_ &&
+ mv T1/[A-Z]* T1/_new_
+}
+
+move_directory_up() {
+ mv T1/T2/T3 T1
+}
+
+move_directory() {
+ mv T1/T2/T3 T1/T2/NewT3
+}
+
# The next few test cases confirm that our fsmonitor daemon sees each type
# of OS filesystem notification that we care about. At this layer we just
# ensure we are getting the OS notifications and do not try to confirm what
@@ -595,6 +707,10 @@ do
matrix_try $uc_val $fsm_val file_to_directory
matrix_try $uc_val $fsm_val directory_to_file
+ matrix_try $uc_val $fsm_val move_directory_contents_deeper
+ matrix_try $uc_val $fsm_val move_directory_up
+ matrix_try $uc_val $fsm_val move_directory
+
if test $fsm_val = true
then
test_expect_success "Matrix[uc:$uc_val][fsm:$fsm_val] disable fsmonitor at end" '
@@ -606,4 +722,281 @@ do
done
done
+# Test Unicode UTF-8 characters in the pathname of the working
+# directory root. Use of "*A()" routines rather than "*W()" routines
+# on Windows can sometimes lead to odd failures.
+#
+u1=$(printf "u_c3_a6__\xC3\xA6")
+u2=$(printf "u_e2_99_ab__\xE2\x99\xAB")
+u_values="$u1 $u2"
+for u in $u_values
+do
+ test_expect_success "unicode in repo root path: $u" '
+ test_when_finished "stop_daemon_delete_repo $u" &&
+
+ git init "$u" &&
+ echo 1 >"$u"/file1 &&
+ git -C "$u" add file1 &&
+ git -C "$u" config core.fsmonitor true &&
+
+ start_daemon -C "$u" &&
+ git -C "$u" status >actual &&
+ grep "new file: file1" actual
+ '
+done
+
+# Test fsmonitor interaction with submodules.
+#
+# If we start the daemon in the super, it will see FS events for
+# everything in the working directory cone and this includes any
+# files/directories contained *within* the submodules.
+#
+# A `git status` at top level will get events for items within the
+# submodule and ignore them, since they aren't named in the index
+# of the super repo. This makes the fsmonitor response a little
+# noisy, but it doesn't alter the correctness of the state of the
+# super-proper.
+#
+# When we have submodules, `git status` normally does a recursive
+# status on each of the submodules and adds a summary row for any
+# dirty submodules. (See the "S..." bits in porcelain V2 output.)
+#
+# It is therefore important that the top level status not be tricked
+# by the FSMonitor response to skip those recursive calls. That is,
+# even if FSMonitor says that the mtime of the submodule directory
+# hasn't changed and it could be implicitly marked valid, we must
+# not take that shortcut. We need to force the recusion into the
+# submodule so that we get a summary of the status *within* the
+# submodule.
+
+create_super () {
+ super="$1" &&
+
+ git init "$super" &&
+ echo x >"$super/file_1" &&
+ echo y >"$super/file_2" &&
+ echo z >"$super/file_3" &&
+ mkdir "$super/dir_1" &&
+ echo a >"$super/dir_1/file_11" &&
+ echo b >"$super/dir_1/file_12" &&
+ mkdir "$super/dir_1/dir_2" &&
+ echo a >"$super/dir_1/dir_2/file_21" &&
+ echo b >"$super/dir_1/dir_2/file_22" &&
+ git -C "$super" add . &&
+ git -C "$super" commit -m "initial $super commit"
+}
+
+create_sub () {
+ sub="$1" &&
+
+ git init "$sub" &&
+ echo x >"$sub/file_x" &&
+ echo y >"$sub/file_y" &&
+ echo z >"$sub/file_z" &&
+ mkdir "$sub/dir_x" &&
+ echo a >"$sub/dir_x/file_a" &&
+ echo b >"$sub/dir_x/file_b" &&
+ mkdir "$sub/dir_x/dir_y" &&
+ echo a >"$sub/dir_x/dir_y/file_a" &&
+ echo b >"$sub/dir_x/dir_y/file_b" &&
+ git -C "$sub" add . &&
+ git -C "$sub" commit -m "initial $sub commit"
+}
+
+my_match_and_clean () {
+ git -C super --no-optional-locks status --porcelain=v2 >actual.with &&
+ git -C super --no-optional-locks -c core.fsmonitor=false \
+ status --porcelain=v2 >actual.without &&
+ test_cmp actual.with actual.without &&
+
+ git -C super/dir_1/dir_2/sub reset --hard &&
+ git -C super/dir_1/dir_2/sub clean -d -f
+}
+
+test_expect_success 'submodule always visited' '
+ test_when_finished "git -C super fsmonitor--daemon stop; \
+ rm -rf super; \
+ rm -rf sub" &&
+
+ create_super super &&
+ create_sub sub &&
+
+ git -C super submodule add ../sub ./dir_1/dir_2/sub &&
+ git -C super commit -m "add sub" &&
+
+ start_daemon -C super &&
+ git -C super config core.fsmonitor true &&
+ git -C super update-index --fsmonitor &&
+ git -C super status &&
+
+ # Now run pairs of commands w/ and w/o FSMonitor while we make
+ # some dirt in the submodule and confirm matching output.
+
+ # Completely clean status.
+ my_match_and_clean &&
+
+ # .M S..U
+ echo z >super/dir_1/dir_2/sub/dir_x/dir_y/foobar_u &&
+ my_match_and_clean &&
+
+ # .M S.M.
+ echo z >super/dir_1/dir_2/sub/dir_x/dir_y/foobar_m &&
+ git -C super/dir_1/dir_2/sub add . &&
+ my_match_and_clean &&
+
+ # .M S.M.
+ echo z >>super/dir_1/dir_2/sub/dir_x/dir_y/file_a &&
+ git -C super/dir_1/dir_2/sub add . &&
+ my_match_and_clean &&
+
+ # .M SC..
+ echo z >>super/dir_1/dir_2/sub/dir_x/dir_y/file_a &&
+ git -C super/dir_1/dir_2/sub add . &&
+ git -C super/dir_1/dir_2/sub commit -m "SC.." &&
+ my_match_and_clean
+'
+
+# If a submodule has a `sub/.git/` directory (rather than a file
+# pointing to the super's `.git/modules/sub`) and `core.fsmonitor`
+# turned on in the submodule and the daemon is not yet started in
+# the submodule, and someone does a `git submodule absorbgitdirs`
+# in the super, Git will recursively invoke `git submodule--helper`
+# to do the work and this may try to read the index. This will
+# try to start the daemon in the submodule *and* pass (either
+# directly or via inheritance) the `--super-prefix` arg to the
+# `git fsmonitor--daemon start` command inside the submodule.
+# This causes a warning because fsmonitor--daemon does take that
+# global arg (see the table in git.c)
+#
+# This causes a warning when trying to start the daemon that is
+# somewhat confusing. It does not seem to hurt anything because
+# the fsmonitor code maps the query failure into a trivial response
+# and does the work anyway.
+#
+# It would be nice to silence the warning, however.
+
+have_t2_error_event () {
+ log=$1
+ msg="fsmonitor--daemon doesnQt support --super-prefix" &&
+
+ tr '\047' Q <$1 | grep -e "$msg"
+}
+
+test_expect_success "stray submodule super-prefix warning" '
+ test_when_finished "rm -rf super; \
+ rm -rf sub; \
+ rm super-sub.trace" &&
+
+ create_super super &&
+ create_sub sub &&
+
+ # Copy rather than submodule add so that we get a .git dir.
+ cp -R ./sub ./super/dir_1/dir_2/sub &&
+
+ git -C super/dir_1/dir_2/sub config core.fsmonitor true &&
+
+ git -C super submodule add ../sub ./dir_1/dir_2/sub &&
+ git -C super commit -m "add sub" &&
+
+ test_path_is_dir super/dir_1/dir_2/sub/.git &&
+
+ GIT_TRACE2_EVENT="$PWD/super-sub.trace" \
+ git -C super submodule absorbgitdirs &&
+
+ ! have_t2_error_event super-sub.trace
+'
+
+# On a case-insensitive file system, confirm that the daemon
+# notices when the .git directory is moved/renamed/deleted
+# regardless of how it is spelled in the the FS event.
+# That is, does the FS event receive the spelling of the
+# operation or does it receive the spelling preserved with
+# the file/directory.
+#
+test_expect_success CASE_INSENSITIVE_FS 'case insensitive+preserving' '
+# test_when_finished "stop_daemon_delete_repo test_insensitive" &&
+
+ git init test_insensitive &&
+
+ start_daemon -C test_insensitive --tf "$PWD/insensitive.trace" &&
+
+ mkdir -p test_insensitive/abc/def &&
+ echo xyz >test_insensitive/ABC/DEF/xyz &&
+
+ test_path_is_dir test_insensitive/.git &&
+ test_path_is_dir test_insensitive/.GIT &&
+
+ # Rename .git using an alternate spelling to verify that that
+ # daemon detects it and automatically shuts down.
+ mv test_insensitive/.GIT test_insensitive/.FOO &&
+
+ # See [1] above.
+ mv test_insensitive/.FOO test_insensitive/.git &&
+
+ verify_implicit_shutdown test_insensitive &&
+
+ # Verify that events were reported using on-disk spellings of the
+ # directories and files that we touched. We may or may not get a
+ # trailing slash on modified directories.
+ #
+ egrep "^event: abc/?$" ./insensitive.trace &&
+ egrep "^event: abc/def/?$" ./insensitive.trace &&
+ egrep "^event: abc/def/xyz$" ./insensitive.trace
+'
+
+# The variable "unicode_debug" is defined in the following library
+# script to dump information about how the (OS, FS) handles Unicode
+# composition. Uncomment the following line if you want to enable it.
+#
+# unicode_debug=true
+
+. "$TEST_DIRECTORY/lib-unicode-nfc-nfd.sh"
+
+# See if the OS or filesystem does NFC/NFD aliasing/munging.
+#
+# The daemon should err on the side of caution and send BOTH the
+# NFC and NFD forms. It does not know the original spelling of
+# the pathname (how the user thinks it should be spelled), so
+# emit both and let the client decide (when necessary). This is
+# similar to "core.precomposeUnicode".
+#
+test_expect_success !UNICODE_COMPOSITION_SENSITIVE 'Unicode nfc/nfd' '
+ test_when_finished "stop_daemon_delete_repo test_unicode" &&
+
+ git init test_unicode &&
+
+ start_daemon -C test_unicode --tf "$PWD/unicode.trace" &&
+
+ # Create a directory using an NFC spelling.
+ #
+ mkdir test_unicode/nfc &&
+ mkdir test_unicode/nfc/c_${utf8_nfc} &&
+
+ # Create a directory using an NFD spelling.
+ #
+ mkdir test_unicode/nfd &&
+ mkdir test_unicode/nfd/d_${utf8_nfd} &&
+
+ git -C test_unicode fsmonitor--daemon stop &&
+
+ if test_have_prereq UNICODE_NFC_PRESERVED
+ then
+ # We should have seen NFC event from OS.
+ # We should not have synthesized an NFD event.
+ egrep "^event: nfc/c_${utf8_nfc}/?$" ./unicode.trace &&
+ egrep -v "^event: nfc/c_${utf8_nfd}/?$" ./unicode.trace
+ else
+ # We should have seen NFD event from OS.
+ # We should have synthesized an NFC event.
+ egrep "^event: nfc/c_${utf8_nfd}/?$" ./unicode.trace &&
+ egrep "^event: nfc/c_${utf8_nfc}/?$" ./unicode.trace
+ fi &&
+
+ # We assume UNICODE_NFD_PRESERVED.
+ # We should have seen explicit NFD from OS.
+ # We should have synthesized an NFC event.
+ egrep "^event: nfd/d_${utf8_nfd}/?$" ./unicode.trace &&
+ egrep "^event: nfd/d_${utf8_nfc}/?$" ./unicode.trace
+'
+
test_done