Similar to t7513, system inotify is taken out to give us better controlled environment. Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@xxxxxxxxx> --- file-watcher-lib.c | 21 ++-- t/t7514-file-watcher-lib.sh (new +x) | 190 +++++++++++++++++++++++++++++++++++ test-file-watcher.c | 15 +++ 3 files changed, 219 insertions(+), 7 deletions(-) create mode 100755 t/t7514-file-watcher-lib.sh diff --git a/file-watcher-lib.c b/file-watcher-lib.c index 93afb52..8cc3b73 100644 --- a/file-watcher-lib.c +++ b/file-watcher-lib.c @@ -23,7 +23,8 @@ static int connect_watcher(const char *path) return fd; } -static void reset_watches(struct index_state *istate, int disconnect) +static void reset_watches(struct index_state *istate, int disconnect, + const char *reason) { int i, changed = 0; if (istate->updated_entries) { @@ -45,6 +46,8 @@ static void reset_watches(struct index_state *istate, int disconnect) close(istate->watcher); istate->watcher = -1; } + trace_printf_key("GIT_TRACE_WATCHER", "reset%s: %s\n", + disconnect ? "/disconnect" : "", reason); } static void mark_ce_valid(struct index_state *istate) @@ -53,15 +56,16 @@ static void mark_ce_valid(struct index_state *istate) char *line, *end; int i, len; unsigned long n; + trace_printf_key("GIT_TRACE_WATCHER", "mark_ce_valid\n"); if (packet_write_timeout(istate->watcher, WAIT_TIME, "get-changed") <= 0 || !(line = packet_read_line_timeout(istate->watcher, WAIT_TIME, &len)) || !starts_with(line, "changed ")) { - reset_watches(istate, 1); + reset_watches(istate, 1, "invalid get-changed response"); return; } n = strtoul(line + 8, &end, 10); if (end != line + len) { - reset_watches(istate, 1); + reset_watches(istate, 1, "invalid get-changed response"); return; } if (!n) @@ -69,7 +73,7 @@ static void mark_ce_valid(struct index_state *istate) strbuf_grow(&sb, n); if (read_in_full_timeout(istate->watcher, sb.buf, n, WAIT_TIME) != n) { strbuf_release(&sb); - reset_watches(istate, 1); + reset_watches(istate, 1, "invalid get-changed payload"); return; } line = sb.buf; @@ -131,7 +135,7 @@ void open_watcher(struct index_state *istate) char *msg; if (!get_git_work_tree()) { - reset_watches(istate, 1); + reset_watches(istate, 1, "no worktree"); return; } @@ -165,10 +169,11 @@ void open_watcher(struct index_state *istate) } istate->watcher = connect_watcher(watcher_path); + trace_printf_key("GIT_TRACE_WATCHER", "open watcher %d\n", istate->watcher); if (packet_write_timeout(istate->watcher, WAIT_TIME, "hello") <= 0 || (msg = packet_read_line_timeout(istate->watcher, WAIT_TIME, NULL)) == NULL || strcmp(msg, "hello")) { - reset_watches(istate, 1); + reset_watches(istate, 1, "invalid hello response"); return; } @@ -177,7 +182,7 @@ void open_watcher(struct index_state *istate) get_git_work_tree()) <= 0 || (msg = packet_read_line_timeout(istate->watcher, WAIT_TIME, NULL)) == NULL || strcmp(msg, "ok")) { - reset_watches(istate, 0); + reset_watches(istate, 0, "inconsistent"); istate->update_watches = 1; return; } @@ -265,6 +270,7 @@ void watch_entries(struct index_state *istate) nr++; if (nr < watch_lowerlimit) return; + trace_printf_key("GIT_TRACE_WATCHER", "watch %d\n", nr); sorted = xmalloc(sizeof(*sorted) * nr); for (i = nr = 0; i < istate->cache_nr; i++) if (ce_watchable(istate->cache[i], now)) @@ -280,6 +286,7 @@ void close_watcher(struct index_state *istate, const unsigned char *sha1) int len, i, nr; if (istate->watcher <= 0) return; + trace_printf_key("GIT_TRACE_WATCHER", "close watcher\n"); if (packet_write_timeout(istate->watcher, WAIT_TIME, "new-index %s", sha1_to_hex(sha1)) <= 0) goto done; diff --git a/t/t7514-file-watcher-lib.sh b/t/t7514-file-watcher-lib.sh new file mode 100755 index 0000000..8dabb13 --- /dev/null +++ b/t/t7514-file-watcher-lib.sh @@ -0,0 +1,190 @@ +#!/bin/sh + +test_description='File watcher daemon and client tests' + +. ./test-lib.sh + +if git file-watcher --check-support && test_have_prereq POSIXPERM; then + : # good +else + skip_all="file-watcher not supported on this system" + test_done +fi + +kill_it() { + test-file-watcher "$1" <<EOF >/dev/null +<die +>see you +EOF +} + +GIT_TEST_WATCHER=2 +GIT_TEST_WATCHER_PATH="$TRASH_DIRECTORY" +export GIT_TEST_WATCHER GIT_TEST_WATCHER_PATH + +test_expect_success 'setup' ' + chmod 700 . && + mkdir foo bar && + touch abc foo/def bar/ghi && + git add . && + git file-watcher --detach . && + cat <<EOF >expect && +<test-mode +>test mode on +EOF + test-file-watcher . <expect >actual && + test_cmp expect actual +' + +test_expect_success 'initial opening sequence' ' + SIG=`test-file-watcher --index-signature .git/index` && + rm actual && + GIT_TRACE_PACKET="$TRASH_DIRECTORY/actual" git ls-files >/dev/null && + cat <<EOF >expect && +packet: git> hello +packet: git< hello +packet: git> index $SIG $TRASH_DIRECTORY +packet: git< inconsistent +packet: git> watch 20 +packet: git< watched 3 +EOF + test_cmp expect actual && + + # second time gives the same result because file-watch has not + # received new-index + GIT_TRACE_PACKET="$TRASH_DIRECTORY/actual2" git ls-files >/dev/null && + test_cmp expect actual2 +' + +test_expect_success 'full sequence' ' + SIG=`test-file-watcher --index-signature .git/index` && + rm actual && + GIT_TRACE_PACKET="$TRASH_DIRECTORY/actual" git status >/dev/null && + SIG2=`test-file-watcher --index-signature .git/index` && + cat <<EOF >expect && +packet: git> hello +packet: git< hello +packet: git> index $SIG $TRASH_DIRECTORY +packet: git< inconsistent +packet: git> watch 20 +packet: git< watched 3 +packet: git> new-index $SIG2 +packet: git> unchange 0 +EOF + test_cmp expect actual +' + +test_expect_success 'full sequence after file-watcher is active' ' + SIG=`test-file-watcher --index-signature .git/index` && + rm actual && + GIT_TRACE_PACKET="$TRASH_DIRECTORY/actual" git ls-files -v >paths.actual && + cat <<EOF >expect && +packet: git> hello +packet: git< hello +packet: git> index $SIG $TRASH_DIRECTORY +packet: git< ok +packet: git> get-changed +packet: git< changed 0 +EOF + test_cmp expect actual && + cat <<EOF >paths.expect && +w abc +w bar/ghi +w foo/def +EOF + test_cmp paths.expect paths.actual +' + +test_expect_success 'inject a file change' ' + echo modified >bar/ghi && + SIG=`test-file-watcher --index-signature .git/index` && + cat >expect <<EOF && +<hello +>hello +<index $SIG $TRASH_DIRECTORY +>ok +<inotify 2 IN_MODIFY ghi +<dump changes +>>changed +>>bar/ghi +EOF + test-file-watcher . >actual <expect && + test_cmp expect actual +' + +test_expect_success 'git obtains the changes' ' + SIG=`test-file-watcher --index-signature .git/index` && + rm actual && + GIT_TEST_WATCHER=1 GIT_TRACE_PACKET="$TRASH_DIRECTORY/actual" git ls-files -v >paths.actual && + cat <<EOF >expect && +packet: git> hello +packet: git< hello +packet: git> index $SIG $TRASH_DIRECTORY +packet: git< ok +packet: git> get-changed +packet: git< changed 8 +EOF + test_cmp expect actual && + cat <<EOF >paths.expect && +w abc +H bar/ghi +w foo/def +EOF + test_cmp paths.expect paths.actual +' + +test_expect_success 'sync file-watcher after index update' ' + SIG=`test-file-watcher --index-signature .git/index` && + rm actual && + GIT_TRACE_PACKET="$TRASH_DIRECTORY/actual" git status --porcelain | grep -vF "??" >paths.actual && + SIG2=`test-file-watcher --index-signature .git/index` && + cat <<EOF >expect && +packet: git> hello +packet: git< hello +packet: git> index $SIG $TRASH_DIRECTORY +packet: git< ok +packet: git> get-changed +packet: git< changed 8 +packet: git> watch 8 +packet: git< watched 1 +packet: git> new-index $SIG2 +packet: git> unchange 8 +EOF + test_cmp expect actual && + cat <<EOF >paths.expect && +A abc +AM bar/ghi +A foo/def +EOF + test_cmp paths.expect paths.actual +' + +test_expect_success 'make sure file-watcher cleans its changed list' ' + SIG=`test-file-watcher --index-signature .git/index` && + rm actual && + GIT_TEST_WATCHER=1 GIT_TRACE_PACKET="$TRASH_DIRECTORY/actual" git ls-files -v >paths.actual && + cat <<EOF >expect && +packet: git> hello +packet: git< hello +packet: git> index $SIG $TRASH_DIRECTORY +packet: git< ok +packet: git> get-changed +packet: git< changed 0 +EOF + test_cmp expect actual && + cat <<EOF >paths.expect && +w abc +H bar/ghi +w foo/def +EOF + test_cmp paths.expect paths.actual +' + +test_expect_success 'closing the daemon' ' + test-file-watcher . <<EOF >/dev/null +<die +>see you +EOF +' + +test_done diff --git a/test-file-watcher.c b/test-file-watcher.c index ffff198..77037e1 100644 --- a/test-file-watcher.c +++ b/test-file-watcher.c @@ -11,6 +11,21 @@ int main(int ac, char **av) int last_command_is_reply = 0; int fd; + if (!strcmp(av[1], "--index-signature")) { + struct stat st; + void *mmap; + if (lstat(av[2], &st)) + die_errno("lstat"); + fd = open(av[2], O_RDONLY); + if (fd < 0) + die_errno("open"); + mmap = xmmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (mmap == MAP_FAILED) + die_errno("mmap"); + printf("%s\n", sha1_to_hex((unsigned char*)mmap + st.st_size - 20)); + return 0; + } + strbuf_addf(&sb, "%s/socket", av[1]); fd = unix_stream_connect(sb.buf); if (fd < 0) -- 1.8.5.2.240.g8478abd -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html